问题描述
有没有一种简单的方法可以通过列表推导来展平可迭代列表,或者如果做不到这一点,你们都认为什么是展平这样的浅层列表、平衡性能和可读性的最佳方法?
Is there a simple way to flatten a list of iterables with a list comprehension, or failing that, what would you all consider to be the best way to flatten a shallow list like this, balancing performance and readability?
我尝试使用嵌套列表理解来展平这样的列表,如下所示:
I tried to flatten such a list with a nested list comprehension, like this:
[image for image in menuitem for menuitem in list_of_menuitems]
但是我遇到了 NameError
种类的问题,因为 name 'menuitem' 没有定义
.在谷歌上搜索并查看 Stack Overflow 后,我通过 reduce
语句得到了想要的结果:
But I get in trouble of the NameError
variety there, because the name 'menuitem' is not defined
. After googling and looking around on Stack Overflow, I got the desired results with a reduce
statement:
reduce(list.__add__, map(lambda x: list(x), list_of_menuitems))
但是这个方法相当不可读,因为我需要在那里调用 list(x)
因为 x 是一个 Django QuerySet
对象.
But this method is fairly unreadable because I need that list(x)
call there because x is a Django QuerySet
object.
结论:
感谢所有为这个问题做出贡献的人.这里是我学到的总结.如果其他人想要添加或更正这些观察结果,我也会将此作为社区 wiki.
Thanks to everyone who contributed to this question. Here is a summary of what I learned. I'm also making this a community wiki in case others want to add to or correct these observations.
我原来的reduce语句是多余的,最好这样写:
My original reduce statement is redundant and is better written this way:
>>> reduce(list.__add__, (list(mi) for mi in list_of_menuitems))
这是嵌套列表理解的正确语法(精彩总结dF!):
This is the correct syntax for a nested list comprehension (Brilliant summary dF!):
>>> [image for mi in list_of_menuitems for image in mi]
但是这些方法都没有使用 itertools.chain
高效:
But neither of these methods are as efficient as using itertools.chain
:
>>> from itertools import chain
>>> list(chain(*list_of_menuitems))
正如@cdleary 所指出的,使用 chain.from_iterable
来避免 * 运算符魔法可能是更好的风格,如下所示:
And as @cdleary notes, it's probably better style to avoid * operator magic by using chain.from_iterable
like so:
>>> chain = itertools.chain.from_iterable([[1,2],[3],[5,89],[],[6]])
>>> print(list(chain))
>>> [1, 2, 3, 5, 89, 6]
推荐答案
如果您只是想迭代数据结构的扁平化版本并且不需要可索引的序列,请考虑 itertools.chain 和公司.
If you're just looking to iterate over a flattened version of the data structure and don't need an indexable sequence, consider 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
,您似乎在问题中使用了它.
It will work on anything that's iterable, which should include Django's iterable QuerySet
s, which it appears that you're using in the question.
这可能与 reduce 一样好,因为 reduce 将具有相同的开销将项目复制到正在扩展的列表中.chain
只有在最后运行 list(chain)
时才会产生这种(相同的)开销.
This is probably as good as a reduce anyway, because reduce will have the same overhead copying the items into the list that's being extended. chain
will only incur this (same) overhead if you run list(chain)
at the end.
元实际上,它的开销比问题提出的解决方案要少,因为当您使用临时列表扩展原始列表时,您会丢弃创建的临时列表.
Meta- Actually, it's less overhead than the question's proposed solution, because you throw away the temporary lists you create when you extend the original with the temporary.
作为 J.F.Sebastian 说 itertools.chain.from_iterable
避免了解包,你应该使用它来避免 *
魔法,但是 timeit 应用 的性能差异可以忽略不计.
As J.F. Sebastian says itertools.chain.from_iterable
avoids the unpacking and you should use that to avoid *
magic, but the timeit app shows negligible performance difference.
这篇关于在 Python 中展平一个浅列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!