如何快速展开包含任意长度列表的列表?
例如,[1, 2, [3, 4, [5],[]], [6]]
应变为 [1,2,3,4,5,6]
。
列表可以有任意多级。列表中的某些对象可能是字符串,在输出列表中不应被展开为单个字符。
如何快速展开包含任意长度列表的列表?
例如,[1, 2, [3, 4, [5],[]], [6]]
应变为 [1,2,3,4,5,6]
。
列表可以有任意多级。列表中的某些对象可能是字符串,在输出列表中不应被展开为单个字符。
这里是一种递归方法,可以处理字符串:
nests = [1, 2, [3, 4, [5],['hi']], [6, [[[7, 'hello']]]]]
def flatten(container):
for i in container:
if isinstance(i, (list,tuple)):
for j in flatten(i):
yield j
else:
yield i
print list(flatten(nests))
返回:
[1, 2, 3, 4, 5, 'hi', 6, 7, 'hello']
请注意,这并不保证速度或开销,但展示了一种递归解决方案,希望能够有所帮助。
yield from flatten(i)
代替for j in flatten(i): yield j
。 - jfsif isinstance(i, (list, tuple)):
来实现。 - acushnerif hasattr(i, '__iter__'):
。 - TayTayhasattr(i, '__iter__')
中导致无限递归。我使用了过滤器 if hasattr(i, '__iter__') and not isinstance(i, str):
来排除这些情况。 - random8递归并非必要。实际上,迭代解决方案通常更快,因为涉及函数调用的开销。这是我一段时间之前编写的迭代版本:
def flatten(items, seqtypes=(list, tuple)):
for i, x in enumerate(items):
while i < len(items) and isinstance(items[i], seqtypes):
items[i:i+1] = items[i]
return items
我还没有测试过这个具体实现的性能,但由于所有的切片赋值可能会导致移动很多内存,所以它可能并不太好。但是,请不要假设它必须是递归的,或者认为用递归更简单。
这个实现有一个优点,就是将列表“原地”铺平,而不像递归解决方案一样返回一个副本。当内存有限时,这可能非常有用。如果您想要一个铺平的副本,只需传入要铺平的列表的浅拷贝即可:
flatten(mylist) # flattens existing list
newlist = flatten(mylist[:]) # makes a flattened copy
此外,这个算法不受Python递归限制的限制,因为它不是递归的。然而,我确信这几乎永远不会发生作用。
2021编辑:我认为检查列表结尾的方法可能最好使用 try
/except
来处理,因为它只会发生一次,并且将测试从主循环中移出可能会提供性能优势。代码如下:
def flatten(items, seqtypes=(list, tuple)):
try:
for i, x in enumerate(items):
while isinstance(items[i], seqtypes):
items[i:i+1] = items[i]
except IndexError:
pass
return items
如果进一步调整代码,使用enumerate
返回的x
而不是频繁访问items[i]
,则可以得到这个结果。在列表的大小和结构不同的情况下,这种方法比上面的原始版本快得多或略微快一些。
def flatten(items, seqtypes=(list, tuple)):
try:
for i, x in enumerate(items):
while isinstance(x, seqtypes):
items[i:i+1] = x
x = items[i]
except IndexError:
pass
return items
enumerate()
非常简单易用,文档中也明确说明它是一种惰性操作(即返回一个迭代器,该迭代器会产生连续的索引值)。同时,iter()
在列表上的行为(使用递增的索引和__getitem__()
进行比较,并在每次迭代时与len()
进行比较,即使长度发生变化也能正常工作)也有文档记录。因此,尽管它看起来有点像黑科技,但实际上除非语言本身发生了重大变化,否则它是相当安全的。 - kindall这个函数应该能够快速地将嵌套的可迭代容器展平,而且不能使用任何递归:
import collections
def flatten(iterable):
iterator = iter(iterable)
array, stack = collections.deque(), collections.deque()
while True:
try:
value = next(iterator)
except StopIteration:
if not stack:
return tuple(array)
iterator = stack.pop()
else:
if not isinstance(value, str) \
and isinstance(value, collections.Iterable):
stack.append(iterator)
iterator = iter(value)
else:
array.append(value)
经过大约五年,我对这个问题的看法已经改变了,也许更好的做法是使用以下方法:
def main():
data = [1, 2, [3, 4, [5], []], [6]]
print(list(flatten(data)))
def flatten(iterable):
iterator, sentinel, stack = iter(iterable), object(), []
while True:
value = next(iterator, sentinel)
if value is sentinel:
if not stack:
break
iterator = stack.pop()
elif isinstance(value, str):
yield value
else:
try:
new_iterator = iter(value)
except TypeError:
yield value
else:
stack.append(iterator)
iterator = new_iterator
if __name__ == '__main__':
main()