在Python中展开一个浅层列表

431

有没有一种简单的方式可以使用列表推导来展开可迭代对象的列表,或者如果无法做到这一点,你们认为最好的展开浅层次列表的方法是什么,需要在性能和可读性之间取得平衡?

我尝试使用嵌套的列表推导来展开这样的列表,像这样:

[image for image in menuitem for menuitem in list_of_menuitems]

但是在那里我遇到了NameError错误,因为name 'menuitem' is not defined。在谷歌搜索和Stack Overflow上查看后,我使用了一个reduce语句来得到所需的结果:

reduce(list.__add__, map(lambda x: list(x), list_of_menuitems))

但是这种方法相当难以阅读,因为我需要在那里调用list(x),因为x是Django QuerySet对象。

结论

感谢所有为这个问题做出贡献的人。以下是我所学到的内容总结。我还将此作为社区维基,以便其他人可以添加或更正这些观察结果。

我的原始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]

31
为什么所有人都在使用map(lambda x: list(x), other)?这不等同于map(list, other)吗?list是个可调用函数... - cdleary
2
@recursive:是的,当你指出我的reduce语句有多少冗余之后,我肯定脸红了。我从这个问题中学到了很多,所以非常感谢大家! - prairiedogg
我已经学习Ruby有一段时间了,最近遇到了一个类似的问题,刚好可以使用Ruby的惯用语法。顺便说一下:[[1,2],[3],[5,89],[],[6]].flatten -> [1, 2, 3, 5, 89, 6] - prairiedogg
1
对于所有列表都为空的情况,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), [])。 - Daira Hopwood
1
这个问题导致了https://dev59.com/qnNA5IYBdhLWcg3wdtld被关闭为重复。然而,由于所有与Django无关的内容,它变得不太清晰。它应该重新编写吗? - Juh_
显示剩余3条评论
23个回答

1
如果列表中的每个项都是字符串(并且这些字符串中的任何字符串使用 " " 而不是 ' '),您可以使用正则表达式(re 模块)。
>>> flattener = re.compile("\'.*?\'")
>>> flattener
<_sre.SRE_Pattern object at 0x10d439ca8>
>>> stred = str(in_list)
>>> outed = flattener.findall(stred)

以上代码将in_list转换为字符串,使用正则表达式查找引号内的所有子字符串(即列表的每个项目),并将它们作为列表输出。

1
一个简单的替代方法是使用numpy的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.]

0
Python 3.4 中,您将能够做到以下事情:
[*innerlist for innerlist in outer_list]

1
嗯,虽然我很欢迎这个功能,但这在Py3.0早就讨论过了。现在有了PEP 448,但仍处于“草案”模式。相关的错误票据仍处于“补丁审核”状态,而且补丁还不完整。在该错误未被标记为“提交”之前,我会谨慎地抱有希望并说“你将能够做到”。 - cfi
我理解你的意思,但最近在Kiwi PyCon 2013上,其中一位核心开发者宣布该功能已被“接受发布”于3.4版本。虽然还不是100%确定,但我认为非常有可能。 - elyase
让我们都希望在发布之前,软件通常是文档落后于代码的问题。;-) - cfi
SyntaxError: can use starred expression only as assignment target - dawg
4
这种语法在最终的 PEP 448 中被否决了。 - dawg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接