在Python中展平一个浅表[重复]
Posted
技术标签:
【中文标题】在Python中展平一个浅表[重复]【英文标题】:Flattening a shallow list in Python [duplicate] 【发布时间】:2010-09-29 04:47:05 【问题描述】:有没有一种简单的方法可以通过列表推导来展平可迭代列表,或者如果做不到这一点,你们都认为什么是展平这样的浅层列表、平衡性能和可读性的最佳方法?
我尝试使用嵌套列表理解来展平这样的列表,如下所示:
[image for image in menuitem for menuitem in list_of_menuitems]
但是我遇到了NameError
的问题,因为name 'menuitem' is not defined
。在 Google 上搜索并查看 Stack Overflow 后,我通过 reduce
声明得到了想要的结果:
reduce(list.__add__, map(lambda x: list(x), list_of_menuitems))
但是这个方法相当不可读,因为我需要在那里调用 list(x)
因为 x 是一个 Django QuerySet
对象。
结论:
感谢所有为这个问题做出贡献的人。这里是我学到的总结。如果其他人想要添加或更正这些观察结果,我也会将此作为社区 wiki。
我原来的reduce语句是多余的,最好这样写:
>>> reduce(list.__add__, (list(mi) for mi in list_of_menuitems))
这是嵌套列表理解的正确语法(精彩总结dF!):
>>> [image for mi in list_of_menuitems for image in mi]
但是这些方法都没有使用itertools.chain
高效:
>>> from itertools import chain
>>> list(chain(*list_of_menuitems))
正如@cdleary 所说,使用chain.from_iterable
来避免 * 运算符魔术可能是更好的风格,如下所示:
>>> chain = itertools.chain.from_iterable([[1,2],[3],[5,89],[],[6]])
>>> print(list(chain))
>>> [1, 2, 3, 5, 89, 6]
【问题讨论】:
我不明白为什么每个人都在使用 map(lambda x: list(x), other) - 这不等同于 map(list, other) 吗?内置列表是可调用的... 是等价的。幸运的是,Prairie Dogg 意识到这段代码很丑陋。 :) @recursive:是的,当你指出我的 reduce 语句有多少东西是多余的时,我肯定脸红了。我确实从这个问题中学到了很多东西,非常感谢大家! reduce(list.__add__, (list(mi.image_set.all()) for mi in list_of_menuitems)) 不适用于所有列表为空的情况。应该是 reduce(list.__add__, (list(mi.image_set.all()) for mi in list_of_menuitems), []) 这个问题使***.com/q/952914/1206998 因重复而关闭。然而,由于所有与 django 无关的东西,它还不太清楚。是否应该重写? 【参考方案1】:如果您只是想迭代数据结构的扁平化版本并且不需要可索引序列,请考虑 itertools.chain and company。
>>> list_of_menuitems = [['image00', 'image01'], ['image10'], []]
>>> import itertools
>>> chain = itertools.chain(*list_of_menuitems)
>>> print(list(chain))
['image00', 'image01', 'image10']
它适用于任何可迭代的事物,其中应包括 Django 的可迭代 QuerySet
s,您似乎在问题中使用了它。
编辑:这可能与 reduce 一样好,因为 reduce 将具有相同的开销将项目复制到正在扩展的列表中。如果您在最后运行 list(chain)
,chain
只会产生这种(相同的)开销。
元编辑:实际上,它的开销比问题提出的解决方案要少,因为当您使用临时列表扩展原始列表时,您会丢弃创建的临时列表。
编辑:因为J.F. Sebastian says itertools.chain.from_iterable
避免了拆包,您应该使用它来避免*
魔术,但the timeit app 显示的性能差异可以忽略不计。
【讨论】:
一个使用.extend
method is the fastest solution according to this benchmark的显式循环
没有收到 from_iterable 的消息。它比 * 更漂亮,如果不那么 Python 的话
还值得强调的是,因为from_iterable
避免了拆包,它可以避免在可迭代项中有许多(可能是无限的)项目的问题。如果 iterable 足够长,就会耗尽内存。【参考方案2】:
你几乎拥有它! way to do nested list comprehensions 是将for
语句的顺序与常规嵌套for
语句中的顺序相同。
所以,这个
for inner_list in outer_list:
for item in inner_list:
...
对应
[... for inner_list in outer_list for item in inner_list]
所以你想要
[image for menuitem in list_of_menuitems for image in menuitem]
【讨论】:
+1,我已经查了很多次了,这是我看到的唯一一个明确排序的答案......也许现在我能记住了! 我希望我能再次投票,因为这种思维方式使嵌套列表推导更容易理解。 而 [... for item in inner_list for inner_list in outer_list] 是一个 Python 陷阱:它只会在 inner_list 的最后一个值上重复[... for item in inner_list]
,并且重复 len(outer_list) 的次数。没用。
这个顺序真的很奇怪。如果你把for i in list: ...
改成... for i in list
,那你为什么不把for循环的顺序也改一下呢?
哈!我又忘记了。我猜 Guido 的大脑和我的大脑在直觉上存在分歧。【参考方案3】:
@S.Lott:你启发了我写了一个 timeit 应用程序。
我认为它也会根据分区数量(容器列表中的迭代器数量)而有所不同——您的评论没有提到三十个项目中有多少个分区。该图在每次运行中都会展平一千个项目,分区数量不同。项目在分区之间均匀分布。
代码(Python 2.6):
#!/usr/bin/env python2.6
"""Usage: %prog item_count"""
from __future__ import print_function
import collections
import itertools
import operator
from timeit import Timer
import sys
import matplotlib.pyplot as pyplot
def itertools_flatten(iter_lst):
return list(itertools.chain(*iter_lst))
def itertools_iterable_flatten(iter_iter):
return list(itertools.chain.from_iterable(iter_iter))
def reduce_flatten(iter_lst):
return reduce(operator.add, map(list, iter_lst))
def reduce_lambda_flatten(iter_lst):
return reduce(operator.add, map(lambda x: list(x), [i for i in iter_lst]))
def comprehension_flatten(iter_lst):
return list(item for iter_ in iter_lst for item in iter_)
METHODS = ['itertools', 'itertools_iterable', 'reduce', 'reduce_lambda',
'comprehension']
def _time_test_assert(iter_lst):
"""Make sure all methods produce an equivalent value.
:raise AssertionError: On any non-equivalent value."""
callables = (globals()[method + '_flatten'] for method in METHODS)
results = [callable(iter_lst) for callable in callables]
if not all(result == results[0] for result in results[1:]):
raise AssertionError
def time_test(partition_count, item_count_per_partition, test_count=10000):
"""Run flatten methods on a list of :param:`partition_count` iterables.
Normalize results over :param:`test_count` runs.
:return: Mapping from method to (normalized) microseconds per pass.
"""
iter_lst = [[dict()] * item_count_per_partition] * partition_count
print('Partition count: ', partition_count)
print('Items per partition:', item_count_per_partition)
_time_test_assert(iter_lst)
test_str = 'flatten(%r)' % iter_lst
result_by_method =
for method in METHODS:
setup_str = 'from test import %s_flatten as flatten' % method
t = Timer(test_str, setup_str)
per_pass = test_count * t.timeit(number=test_count) / test_count
print('%20s: %.2f usec/pass' % (method, per_pass))
result_by_method[method] = per_pass
return result_by_method
if __name__ == '__main__':
if len(sys.argv) != 2:
raise ValueError('Need a number of items to flatten')
item_count = int(sys.argv[1])
partition_counts = []
pass_times_by_method = collections.defaultdict(list)
for partition_count in xrange(1, item_count):
if item_count % partition_count != 0:
continue
items_per_partition = item_count / partition_count
result_by_method = time_test(partition_count, items_per_partition)
partition_counts.append(partition_count)
for method, result in result_by_method.iteritems():
pass_times_by_method[method].append(result)
for method, pass_times in pass_times_by_method.iteritems():
pyplot.plot(partition_counts, pass_times, label=method)
pyplot.legend()
pyplot.title('Flattening Comparison for %d Items' % item_count)
pyplot.xlabel('Number of Partitions')
pyplot.ylabel('Microseconds')
pyplot.show()
编辑:决定将其设为社区 wiki。
注意: METHODS
可能应该与装饰器一起累积,但我认为人们这样阅读会更容易。
【讨论】:
试试sum_flatten = lambda iter_lst: sum(map(list, iter_lst), [])
或者只是 sum(list, [])
@EnTerr 建议reduce(operator.iadd
***.com/questions/3040335/… 是目前最快的(代码:ideone.com/NWThp 图片:i403.photobucket.com/albums/pp111/uber_ulrich/p1000.png)
chain.from_iterable()
如果有很多分区i403.photobucket.com/albums/pp111/uber_ulrich/p10000.png会稍快
我知道这是一个旧线程,但我添加了一个从here 获得的方法,它使用 list.extend,它已被证明是最快的。 graphupdated gist【参考方案4】:
sum(list_of_lists, [])
会将其展平。
l = [['image00', 'image01'], ['image10'], []]
print sum(l,[]) # prints ['image00', 'image01', 'image10']
【讨论】:
我喜欢!它让我想起了使用iter[::-1]
而不是sorted(iter, reverse=True)
。我想知道这是否是多年来被审查为“糟糕的 Python”的事情之一。我觉得它是一个非常 TIMTOWTDI 的解决方案。【参考方案5】:
此解决方案适用于任意嵌套深度 - 不仅仅是“列表列表”深度,某些(全部?)其他解决方案仅限于:
def flatten(x):
result = []
for el in x:
if hasattr(el, "__iter__") and not isinstance(el, basestring):
result.extend(flatten(el))
else:
result.append(el)
return result
这是允许任意深度嵌套的递归——当然,直到你达到最大递归深度......
【讨论】:
可能值得添加hasattr(el, '__getitem__')
以兼容 iter()
函数和内置 for-in 循环(尽管所有 Python 序列(带有 __getitem__
的对象)也是可迭代的(带有 __iter__
的对象))。
我期待在 itertools 中已经有类似的东西。有没有类似的使用推导式的解决方案?
这对我来说是最有用的,因为它不会分隔字符串。
@JosepVallsm 不错的解决方案!对于 python3 你需要使用str
而不是basestring
, The builtin basestring abstract type was removed. Use str instead. The str and bytes types don’t have functionality enough in common to warrant a shared base class. The 2to3 tool (see below) replaces every occurrence of basestring with str.
@JosepValls,你能告诉我为什么类似的method like yours 给出RECURSION ERROR ON
输入A = ['str1', [[[['str2']]]], [['str3'], 'str4'], 'str5'] and input
A = [1.0, 2, 'a', (4,), ((6 ,), (8,)), (((8,),(9,)), ((12,),(10)))]`,但你的解决方案可以正常工作!【参考方案6】:
在 Python 2.6 中,使用 chain.from_iterable()
:
>>> from itertools import chain
>>> list(chain.from_iterable(mi.image_set.all() for mi in h.get_image_menu()))
它避免了中间列表的创建。
【讨论】:
【参考方案7】:性能结果。已修改。
import itertools
def itertools_flatten( aList ):
return list( itertools.chain(*aList) )
from operator import add
def reduce_flatten1( aList ):
return reduce(add, map(lambda x: list(x), [mi for mi in aList]))
def reduce_flatten2( aList ):
return reduce(list.__add__, map(list, aList))
def comprehension_flatten( aList ):
return list(y for x in aList for y in x)
我将包含 30 个项目的 2 级列表展平 1000 次
itertools_flatten 0.00554
comprehension_flatten 0.00815
reduce_flatten2 0.01103
reduce_flatten1 0.01404
减少总是一个糟糕的选择。
【讨论】:
map(lambda x: list(x), [mi for mi in aList]))
是map(list, aList)
。
reduce_flatten = lambda list_of_iters: reduce(list.__add__, map(list, list_of_iters))
itertools_flatten2 = lambda aList: list(itertools.chain.from_iterable(aList))
在 2.5.2 中没有 chain.from_iterable -- 抱歉 -- 无法与其他解决方案进行比较。
@recursive 的版本:sum_flatten = lambda aList: sum(map(list, aList), [])
【参考方案8】:
似乎与operator.add
混淆了!当您将两个列表相加时,正确的术语是concat
,而不是相加。 operator.concat
是你需要使用的。
如果您正在考虑功能性,那就这么简单::
>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)
你看到 reduce 尊重序列类型,所以当你提供一个元组时,你会得到一个元组。让我们尝试一个列表::
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
啊哈,你拿回了一份清单。
性能怎么样::
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop
from_iterable 非常快!但是用concat来reduce是没有可比性的。
>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop
【讨论】:
这可能是一层嵌套的最佳解决方案。但这可能是一个过于严格的约束。 YMMV【参考方案9】:在我的脑海中,你可以消除 lambda:
reduce(list.__add__, map(list, [mi.image_set.all() for mi in list_of_menuitems]))
甚至消除地图,因为您已经有了一个列表组合:
reduce(list.__add__, [list(mi.image_set.all()) for mi in list_of_menuitems])
您也可以将其表示为列表的总和:
sum([list(mi.image_set.all()) for mi in list_of_menuitems], [])
【讨论】:
你可以只使用 add,我认为 sum 的第二个参数是多余的。 这不是多余的。默认为零,产生 TypeError: unsupported operand type(s) for +: 'int' and 'list'。 IMO sum() 比 reduce(add, ...) 更直接【参考方案10】:这是使用列表推导的正确解决方案(它们在问题中落后):
>>> join = lambda it: (y for x in it for y in x)
>>> list(join([[1,2],[3,4,5],[]]))
[1, 2, 3, 4, 5]
在你的情况下是
[image for menuitem in list_of_menuitems for image in menuitem.image_set.all()]
或者你可以使用join
并说
join(menuitem.image_set.all() for menuitem in list_of_menuitems)
无论哪种情况,问题都是 for
循环的嵌套。
【讨论】:
【参考方案11】:这个版本是一个生成器。如果你想要一个列表,可以调整它。
def list_or_tuple(l):
return isinstance(l,(list,tuple))
## predicate will select the container to be flattened
## write your own as required
## this one flattens every list/tuple
def flatten(seq,predicate=list_or_tuple):
## recursive generator
for i in seq:
if predicate(seq):
for j in flatten(i):
yield j
else:
yield i
如果想要展平满足条件的谓词,可以添加谓词
取自 python 食谱
【讨论】:
【参考方案12】:如果您必须使用不可迭代的元素或深度超过 2 的更复杂的列表进行展平,您可以使用以下函数:
def flat_list(list_to_flat):
if not isinstance(list_to_flat, list):
yield list_to_flat
else:
for item in list_to_flat:
yield from flat_list(item)
它将返回一个生成器对象,您可以使用list()
函数将其转换为列表。请注意,yield from
语法从 python3.3 开始可用,但您可以改用显式迭代。
示例:
>>> a = [1, [2, 3], [1, [2, 3, [1, [2, 3]]]]]
>>> print(list(flat_list(a)))
[1, 2, 3, 1, 2, 3, 1, 2, 3]
【讨论】:
这个解决方案给出了RECURSION ERROR ON :
输入A = ['str1', [[[['str2']]]], [['str3'], 'str4'], 'str5']
和A = [1.0, 2, 'a', [4,], [[6,], [8,]], [[[8,],[9,]], [[12,],[10]]]]
。你知道为什么以及如何解决它吗?
@anu 它在你的例子中对我来说没有错误(python 3.7.1)。我不知道为什么它对你不起作用。
我用的是python3.6,我现在发现了问题,你需要在第一个if条件中添加or isinstance(list_to_flat, str)
,因为它必须防止字符串。您的解决方案非常适合输入 A = [1, [[[[2]]]], [[3], 4], 5]
,但在使用字符串时失败!是否在 python3.7 中使用字符串进行了测试?
@anu 我在您提供的完全相同的示例上对其进行了测试。你的第一个例子是字符串,它工作得很好。第一个 if 语句表示按原样返回任何非列表项,而不进行展平。这也包括字符串,不需要额外的条件。
哦,好吧,可能是因为python版本不同!他们可能在 3.7 中推出了一些更新【参考方案13】:
这是一个使用collectons.Iterable
处理多级列表的版本:
import collections
def flatten(o, flatten_condition=lambda i: isinstance(i,
collections.Iterable) and not isinstance(i, str)):
result = []
for i in o:
if flatten_condition(i):
result.extend(flatten(i, flatten_condition))
else:
result.append(i)
return result
【讨论】:
能否建议您的解决方案为何在此输入A = ['image1', [[[['image2']]]], [['image3'], 'image4'], 'image5']
上给出 RecursionError: maximum recursion depth exceeded in comparison
,而它运行良好且不平整此输入 A = [1,[2,3],[4,5,[6,[7,8],9]]]
扁平化条件有问题。由于字符串是可迭代的,因此它们被展平为字符,这些字符本身就是长度为 1 的字符串,并且因为它们是字符串,所以再次应用相同的逻辑并创建一个无限循环。因此,我创建了一个具有展平条件的新版本,以便进行更多控制。
太棒了!非常感谢您的澄清,它现在正在工作。!我有点理解你的推理,但无法完全消化。您能否指出我在网络上的一些文章或任何有助于理解其问题的帖子!我的理解是` ['image1'] -->['i','m','a','g','e','1'] `即长度为1的字符串!,现在如何会进入无限循环,什么会进入无限循环?那部分我还没看懂!你能帮忙吗!
对于函数flatten to end,如果它进入for循环,它需要在某个时候进入else语句。如果它进入 else 语句,那么它将开始展开调用堆栈并返回一个结果。基于以前的版本,因为“image1”是可迭代的,所以 o 将等于“image1”,而 i 将等于“i”。 'i' 也是可迭代的,因此在下一次调用中 o 将等于 'i' 而 i 也将等于 'i'。该函数将被再次调用,导致完全相同的状态和一个仅由堆栈溢出中断的无限循环。
最好使用yield
通过result
列表生成项目序列。迭代器可以被延迟计算,使用它的 fn 可以根据需要使用序列。【参考方案14】:
你试过扁平化吗? 来自matplotlib.cbook.flatten(seq, scalarp=)?
l=[[1,2,3],[4,5,6], [7], [8,9]]*33
run("list(flatten(l))")
3732 function calls (3303 primitive calls) in 0.007 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.007 0.007 <string>:1(<module>)
429 0.001 0.000 0.001 0.000 cbook.py:475(iterable)
429 0.002 0.000 0.003 0.000 cbook.py:484(is_string_like)
429 0.002 0.000 0.006 0.000 cbook.py:565(is_scalar_or_string)
727/298 0.001 0.000 0.007 0.000 cbook.py:605(flatten)
429 0.000 0.000 0.001 0.000 core.py:5641(isMaskedArray)
858 0.001 0.000 0.001 0.000 isinstance
429 0.000 0.000 0.000 0.000 iter
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
l=[[1,2,3],[4,5,6], [7], [8,9]]*66
run("list(flatten(l))")
7461 function calls (6603 primitive calls) in 0.007 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.007 0.007 <string>:1(<module>)
858 0.001 0.000 0.001 0.000 cbook.py:475(iterable)
858 0.002 0.000 0.003 0.000 cbook.py:484(is_string_like)
858 0.002 0.000 0.006 0.000 cbook.py:565(is_scalar_or_string)
1453/595 0.001 0.000 0.007 0.000 cbook.py:605(flatten)
858 0.000 0.000 0.001 0.000 core.py:5641(isMaskedArray)
1716 0.001 0.000 0.001 0.000 isinstance
858 0.000 0.000 0.000 0.000 iter
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
l=[[1,2,3],[4,5,6], [7], [8,9]]*99
run("list(flatten(l))")
11190 function calls (9903 primitive calls) in 0.010 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.010 0.010 <string>:1(<module>)
1287 0.002 0.000 0.002 0.000 cbook.py:475(iterable)
1287 0.003 0.000 0.004 0.000 cbook.py:484(is_string_like)
1287 0.002 0.000 0.009 0.000 cbook.py:565(is_scalar_or_string)
2179/892 0.001 0.000 0.010 0.000 cbook.py:605(flatten)
1287 0.001 0.000 0.001 0.000 core.py:5641(isMaskedArray)
2574 0.001 0.000 0.001 0.000 isinstance
1287 0.000 0.000 0.000 0.000 iter
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
l=[[1,2,3],[4,5,6], [7], [8,9]]*132
run("list(flatten(l))")
14919 function calls (13203 primitive calls) in 0.013 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.013 0.013 <string>:1(<module>)
1716 0.002 0.000 0.002 0.000 cbook.py:475(iterable)
1716 0.004 0.000 0.006 0.000 cbook.py:484(is_string_like)
1716 0.003 0.000 0.011 0.000 cbook.py:565(is_scalar_or_string)
2905/1189 0.002 0.000 0.013 0.000 cbook.py:605(flatten)
1716 0.001 0.000 0.001 0.000 core.py:5641(isMaskedArray)
3432 0.001 0.000 0.001 0.000 isinstance
1716 0.001 0.000 0.001 0.000 iter
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler'
更新 这给了我另一个想法:
l=[[1,2,3],[4,5,6], [7], [8,9]]*33
run("flattenlist(l)")
564 function calls (432 primitive calls) in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
133/1 0.000 0.000 0.000 0.000 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
429 0.000 0.000 0.000 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
l=[[1,2,3],[4,5,6], [7], [8,9]]*66
run("flattenlist(l)")
1125 function calls (861 primitive calls) in 0.001 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
265/1 0.001 0.000 0.001 0.001 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.000 0.000 0.001 0.001 <string>:1(<module>)
858 0.000 0.000 0.000 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
l=[[1,2,3],[4,5,6], [7], [8,9]]*99
run("flattenlist(l)")
1686 function calls (1290 primitive calls) in 0.001 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
397/1 0.001 0.000 0.001 0.001 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.000 0.000 0.001 0.001 <string>:1(<module>)
1287 0.000 0.000 0.000 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
l=[[1,2,3],[4,5,6], [7], [8,9]]*132
run("flattenlist(l)")
2247 function calls (1719 primitive calls) in 0.002 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
529/1 0.001 0.000 0.002 0.002 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.000 0.000 0.002 0.002 <string>:1(<module>)
1716 0.001 0.000 0.001 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
l=[[1,2,3],[4,5,6], [7], [8,9]]*1320
run("flattenlist(l)")
22443 function calls (17163 primitive calls) in 0.016 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
5281/1 0.011 0.000 0.016 0.016 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.000 0.000 0.016 0.016 <string>:1(<module>)
17160 0.005 0.000 0.005 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
所以要测试当递归变得更深时它的有效性:有多深?
l=[[1,2,3],[4,5,6], [7], [8,9]]*1320
new=[l]*33
run("flattenlist(new)")
740589 function calls (566316 primitive calls) in 0.418 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
174274/1 0.281 0.000 0.417 0.417 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.001 0.001 0.418 0.418 <string>:1(<module>)
566313 0.136 0.000 0.136 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
new=[l]*66
run("flattenlist(new)")
1481175 function calls (1132629 primitive calls) in 0.809 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
348547/1 0.542 0.000 0.807 0.807 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.002 0.002 0.809 0.809 <string>:1(<module>)
1132626 0.266 0.000 0.266 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
new=[l]*99
run("flattenlist(new)")
2221761 function calls (1698942 primitive calls) in 1.211 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
522820/1 0.815 0.000 1.208 1.208 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.002 0.002 1.211 1.211 <string>:1(<module>)
1698939 0.393 0.000 0.393 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
new=[l]*132
run("flattenlist(new)")
2962347 function calls (2265255 primitive calls) in 1.630 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
697093/1 1.091 0.000 1.627 1.627 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.003 0.003 1.630 1.630 <string>:1(<module>)
2265252 0.536 0.000 0.536 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
new=[l]*1320
run("flattenlist(new)")
29623443 function calls (22652523 primitive calls) in 16.103 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
6970921/1 10.842 0.000 16.069 16.069 <ipython-input-55-39b139bad497>:4(flattenlist)
1 0.034 0.034 16.103 16.103 <string>:1(<module>)
22652520 5.227 0.000 5.227 0.000 isinstance
1 0.000 0.000 0.000 0.000 method 'disable' of '_lsprof.Profiler' objects
我敢打赌“flattenlist”我会使用它而不是 matploblib 很长一段时间,除非我想要一个产量生成器和快速的结果,因为“flatten”在 matploblib.cbook 中使用
这,很快。
这是代码:
typ=(list,tuple)
def flattenlist(d):
thelist = []
for x in d:
if not isinstance(x,typ):
thelist += [x]
else:
thelist += flattenlist(x)
return thelist
【讨论】:
【参考方案15】:根据我的经验,扁平化列表的最有效方法是:
flat_list = []
map(flat_list.extend, list_of_list)
与其他建议方法的一些时间比较:
list_of_list = [range(10)]*1000
%timeit flat_list=[]; map(flat_list.extend, list_of_list)
#10000 loops, best of 3: 119 µs per loop
%timeit flat_list=list(itertools.chain.from_iterable(list_of_list))
#1000 loops, best of 3: 210 µs per loop
%timeit flat_list=[i for sublist in list_of_list for i in sublist]
#1000 loops, best of 3: 525 µs per loop
%timeit flat_list=reduce(list.__add__,list_of_list)
#100 loops, best of 3: 18.1 ms per loop
现在,在处理较长的子列表时效率提升会更好:
list_of_list = [range(1000)]*10
%timeit flat_list=[]; map(flat_list.extend, list_of_list)
#10000 loops, best of 3: 60.7 µs per loop
%timeit flat_list=list(itertools.chain.from_iterable(list_of_list))
#10000 loops, best of 3: 176 µs per loop
而且此方法也适用于任何迭代对象:
class SquaredRange(object):
def __init__(self, n):
self.range = range(n)
def __iter__(self):
for i in self.range:
yield i**2
list_of_list = [SquaredRange(5)]*3
flat_list = []
map(flat_list.extend, list_of_list)
print flat_list
#[0, 1, 4, 9, 16, 0, 1, 4, 9, 16, 0, 1, 4, 9, 16]
【讨论】:
【参考方案16】:def is_iterable(item):
return isinstance(item, list) or isinstance(item, tuple)
def flatten(items):
for i in items:
if is_iterable(item):
for m in flatten(i):
yield m
else:
yield i
测试:
print list(flatten2([1.0, 2, 'a', (4,), ((6,), (8,)), (((8,),(9,)), ((12,),(10)))]))
【讨论】:
这可能会将字符串扁平化为单个字符,这可能不是预期的行为? 是的,我没有考虑这种情况。谢谢。 @kopos,感谢您的解决方案,但是,我在您的输入A = [1.0, 2, 'a', (4,), ((6,), (8,)), (((8,),(9,)), ((12,),(10)))]
和 A = ['str1', [[[['str2']]]], [['str3'], 'str4'], 'str5']
上收到此错误 for m in flatten(i): [Previous line repeated 996 more times] RecursionError: maximum recursion depth exceeded
,但在此输入 A = [1, [[[[2]]]], [[3], 4], 5]
上工作正常。你知道它失败的原因是什么吗?以及如何解决?有什么建议吗?
@kopos,我现在得到了修复!,您需要在 if 语句 and not isinstance(i,str )
中再添加一个条件,以防止扁平化时列表中的字符串!
@anu:是的,修复有效!但问题是,我们是根据hasattr
和isinstance
来识别集合类型。如果我们知道集合节点的类型,那么 fn 可以定制为相同的。如果集合是set
,我们可能还需要根据它的行为方式来调整函数【参考方案17】:
怎么样:
from operator import add
reduce(add, map(lambda x: list(x.image_set.all()), [mi for mi in list_of_menuitems]))
但是,Guido 建议不要在一行代码中执行过多的代码,因为这会降低可读性。通过在单行与多行中执行您想要的操作,性能提升很小(如果有的话)。
【讨论】:
在一行中完成一些疯狂的工作令人难以置信的满足......但它真的只是语法糖 如果我没记错的话,Guido 实际上也建议不要使用归约和列表推导式......虽然我不同意,但它们非常有用。 检查这个小块与多行函数的性能。我想你会发现这条单线是一只真正的狗。 可能,使用 lambdas 映射是可怕的。每个函数调用所产生的开销会耗尽您的代码的生命。我从来没有说过那条特定的线路和多线路解决方案一样快...... ;)【参考方案18】:pylab 提供了一个扁平化: link to numpy flatten
【讨论】:
注意:Flatten 不适用于锯齿状数组。尝试改用hstack。【参考方案19】:如果您正在寻找一种内置的、简单的、单行的,您可以使用:
a = [[1, 2, 3], [4, 5, 6]
b = [i[x] for i in a for x in range(len(i))]
print b
返回
[1, 2, 3, 4, 5, 6]
【讨论】:
【参考方案20】:如果列表中的每个项目都是一个字符串(并且这些字符串中的任何字符串都使用“”而不是''),您可以使用正则表达式(re
模块)
>>> flattener = re.compile("\'.*?\'")
>>> flattener
<_sre.SRE_Pattern object at 0x10d439ca8>
>>> stred = str(in_list)
>>> outed = flattener.findall(stred)
上述代码将 in_list 转换为字符串,使用正则表达式查找引号内的所有子字符串(即列表的每个项目)并将它们作为列表吐出。
【讨论】:
【参考方案21】:一个简单的替代方法是使用numpy's concatenate,但它会将内容转换为浮点数:
import numpy as np
print np.concatenate([[1,2],[3],[5,89],[],[6]])
# array([ 1., 2., 3., 5., 89., 6.])
print list(np.concatenate([[1,2],[3],[5,89],[],[6]]))
# [ 1., 2., 3., 5., 89., 6.]
【讨论】:
【参考方案22】:在 Python 2 或 3 中实现此目的的最简单方法是使用 morph 库和 pip install morph
。
代码是:
import morph
list = [[1,2],[3],[5,89],[],[6]]
flattened_list = morph.flatten(list) # returns [1, 2, 3, 5, 89, 6]
【讨论】:
“最简单”是a strong word @cfi 您建议的答案在 Python 2 中不起作用,并且从 cmets 听起来它甚至不是 Python 3 中可接受的答案。变形库是一个简单的函数解决方案,例如你有 lodash 的 javascript。无论如何,我编辑了我的答案以澄清它是适用于 Python 2 和 3 的最简单的解决方案。 我很抱歉。我的评论有点懒惰,特别是因为你在另一篇文章中指出了我自己的评论。我想说的是,“最简单”是一种很难实现的***。您的建议需要一个外部库,对于某些人来说可能很难安装(即使使用 venv 等)。由于问题是关于“浅层”列表和“平衡性能和可读性”,因此您的答案可能(!)赢得了可读性。但是this one 在性能上胜出,并且更容易,因为它不需要依赖。 @cfi 是的 - 我的可能是“懒人的方法”。对我来说,看到所有这些扁平化方式让我只想找到一个快速的库命令,就像我在 morph 中找到的那样。这个库的好处是它比 numpy 小得多(我必须使用交换文件在小型服务器实例上安装 numpy)。它基本上使用您在第二条评论中描述的功能;另一种选择是让我在我的代码中使用它作为辅助函数。完全没问题,感谢您指出选项:)。【参考方案23】:在Python 3.4你将能够做到:
[*innerlist for innerlist in outer_list]
【讨论】:
嗯。虽然我对此表示欢迎,但这已经在 Py3.0 中讨论过了。现在PEP 448 在那里,但仍处于“草稿”模式。相关的bug ticket 仍处于“补丁审查”中,补丁尚未完成。在错误没有被标记为“已提交”之前,我会小心地抱起希望并说“你将能够做到”。 我明白你的意思,但最近在 Kiwi PyCon 2013 上,一位核心开发人员在 3.4 中宣布“接受发布”。仍然不是 100% 确定,但我想很有可能。 我们都希望这只是在任何版本发布之前缺少 sw 代码的文档 ;-)SyntaxError: can use starred expression only as assignment target
这个语法在最终的 PEP 448 中是 not accepted以上是关于在Python中展平一个浅表[重复]的主要内容,如果未能解决你的问题,请参考以下文章