为什么Python没有列表的“扁平化”功能?


39

Erlang和Ruby都具有用于平坦化数组的功能。似乎是一种添加到语言的简单实用的工具。一个可以做到这一点:

>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> mess.flatten()
[1, 2, 3, 4, 5, 6]

甚至:

>>> import itertools
>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> list(itertools.flatten(mess))
[1, 2, 3, 4, 5, 6]

取而代之的是,在Python中,必须经历编写从头开始使数组变平的函数的麻烦。对我来说这似乎很愚蠢,将数组展平是一件很平常的事情。这就像必须编写一个自定义函数来连接两个数组。

我已经无能为力地搜索了Google,所以我在这里问。为什么有一个特定的原因,为什么像Python 3这样的成熟语言(附带十万种不同的电池)无法提供简化数组的简单方法?是否曾经讨论过并拒绝包含这种功能的想法?


2
@detly:最近,当我使用多个查询从不同来源检索数据时,我碰巧错过了展平。每个查询都返回一个字典列表,因此最后我得到了一个字典列表列表,这些列表将变成字典列表。我使用了一个循环+,extend但扁平化会更加优雅。但是,如果这种模式足够普遍,足以证明在标准库中变平,我就很伤心。
Giorgio 2014年

4
“我的意思是,想象一下,如果您在代码中引入了一个错误地改变了数据结构的错误。flatten仍然可以工作,但是会产生完全错误的结果。”:这就是为什么我喜欢静态类型的语言的原因之一。;-)
乔治(Giorgio)


2
@BryanOakley见之前的评论,以及(虽然不是多级列表,在一般的扁平化常见的)
Izkata

3
它内置在Mathemaica中,我广泛使用它。
Per Alexandersson

Answers:


34

关于将flatten功能添加到标准库的建议有时会出现在python-devpython-ideas邮件列表中。Python开发人员通常会回答以下几点:

  1. 一级展平(将可迭代的可迭代对象转换为单个可迭代对象)是一个琐碎的单行表达式,(x for y in z for x in y)并且无论如何在标准库中已经存在该名称itertools.chain.from_iterable

  2. 通用多层拼合的用例是什么?这些功能是否真的足以将功能添加到标准库中?

  3. 通用多级拼合如何决定何时拼合以及何时单独放置?您可能会认为诸如“拉平支持可迭代接口的所有内容”之类的规则会起作用,但这将导致的无限循环flatten('a')

参见例如Raymond Hettinger

已经在comp.lang.python上对ad nauseam进行了讨论。人们似乎更喜欢编写自己的flatten版本,而不是查找还没有平凡解决方案的合法用例。

通用拼合器需要某种方式来告知什么是原子的,哪些可以进一步细分。同样,将算法扩展到涵盖树状数据结构的输入以及节点和叶子(预排序,后排序,有序遍历等)上的数据,这一点也不明显。


明确地说,这意味着flatten可以将一级函数定义为lambda z: [x for y in z for x in y]
Christopher Martin

1
“通用拼合器需要某种方式来告知什么是原子的,什么可以进一步细分。”:这听起来像是可以使用OOP解决的问题:每个对象都可以有一个flatten方法。flatten如果对象是复合对象,则此方法的实现应递归调用其子组件。不幸的是,AFAIK并非每个值都是Python中的对象。在Ruby中,它应该可以工作。
乔治

1
国际海事组织已经足够了一个用于单层扁平化而不是持续进行“ for for for in”的扁平化助手。易于阅读
dtc

2
@Giorgio Python避开了此类方法。协议是首选,我觉得他们是极大地平滑,工作比一个面向对象的设计,因为你经常甚至不需要实现非常多的。
jpmc26

8

它确实带有这样的方法,但是没有将其称为flatten。它被称为“ 连锁 ”。它返回一个迭代器,然后您需要使用list()函数将其转换回列表。如果您不想使用*,则可以使用第二个“ from_iterator”版本。它在Python 3中的工作原理相同。如果列表输入不是列表列表,则失败。

[[1], [2, 3], [3, 4, 5]] #yes
[1, 2, [5, 6]] #no

曾经在compile.ast模块中有一个扁平化方法,但是该方法在2.6中已弃用,然后在3.0中被删除。任意深度列表所必需的任意深度递归在Python的保守最大递归深度下不能很好地发挥作用。删除编译器的原因主要是因为它很乱。编译器变成ast,但是变平了。

可以使用numpy的数组和该库的扁平化来实现任意深度。


chain.from_iterator如您所说,该函数只能用于展平二维列表。一个actualy扁平化功能,它接受任何嵌套的列表,并返回一个一维列表的数量,仍然是在很多情况下(至少在我看来)大量有用的
Hubro

2
@Hubro:“在很多情况下”-您能说出六个吗?
Gareth Rees 2014年

1
@GarethRees:我做了几个例子在这里:programmers.stackexchange.com/questions/254279/...
Hubro

我还要争论的是,如果这些其他语言确实提供了这样一种功能,即可以以非常简单的描述方式来平滑列表,那是支持将这种简单功能添加到Python的最引人注目的论据之一。
Bobort

它返回迭代器还是生成器?
jpmc26

-1

...也许是因为自己写书并不难

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

...然后将所有您想要的东西弄平:)

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]
>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 

8
asker意识到:“在Python中,必须经历编写从头开始使数组变平的函数的麻烦”。这甚至没有试图解决所问的问题,“对我来说这似乎很愚蠢,将数组展平是一件很平常的事情。这就像必须编写一个自定义函数来连接两个数组。”
t

1
没话题...但是超级酷:-)!
SeF

这个答案就像告诉OP他不是一个好的开发人员,因为他不知道如何自己编写函数。我建议您修改答案的开头,因为对于那些偶然发现问题的人来说,这是有用的代码,即使是题外话
Federico Bonelli
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.