这里是另一种基于py2的方法,我不确定它是否最快、最优雅或最安全...
from collections import Iterable
from itertools import imap, repeat, chain
def flat(seqs, ignore=(int, long, float, basestring)):
return repeat(seqs, 1) if any(imap(isinstance, repeat(seqs), ignore)) or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))
它可以忽略任何您想要的特定(或派生)类型,它返回一个迭代器,因此您可以将其转换为任何特定容器,例如列表、元组、字典或仅消耗它以减少内存占用,无论好坏它都可以处理最初的非可迭代对象,如int ...
请注意,大部分重活都是在C中完成的,因为据我所知这就是itertools的实现方式,因此虽然它是递归的,但据我所知它不受Python递归深度的限制,因为函数调用发生在C中,尽管这并不意味着您受到内存的限制,特别是在OS X中,由于今天(OS X Mavericks)其堆栈大小有一个硬限制...
有一种稍微更快的方法,但是不太便携,只有在您可以假定输入的基本元素可以被明确确定时才使用它,否则您将得到无限递归,并且具有其有限堆栈大小的OS X将很快抛出分段错误...
def flat(seqs, ignore={int, long, float, str, unicode}):
return repeat(seqs, 1) if type(seqs) in ignore or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))
在这里,我们使用集合来检查类型,因此检查元素是否应被忽略仅需O(1)而不是O(类型数量),但当然,任何具有所述被忽略类型的派生类型的值都将失败,这就是为什么它使用
str
,
unicode
,因此请谨慎使用...
测试:
import random
def test_flat(test_size=2000):
def increase_depth(value, depth=1):
for func in xrange(depth):
value = repeat(value, 1)
return value
def random_sub_chaining(nested_values):
for values in nested_values:
yield chain((values,), chain.from_iterable(imap(next, repeat(nested_values, random.randint(1, 10)))))
expected_values = zip(xrange(test_size), imap(str, xrange(test_size)))
nested_values = random_sub_chaining((increase_depth(value, depth) for depth, value in enumerate(expected_values)))
assert not any(imap(cmp, chain.from_iterable(expected_values), flat(chain(((),), nested_values, ((),)))))
>>> test_flat()
>>> list(flat([[[1, 2, 3], [4, 5]], 6]))
[1, 2, 3, 4, 5, 6]
>>>
$ uname -a
Darwin Samys-MacBook-Pro.local 13.3.0 Darwin Kernel Version 13.3.0: Tue Jun 3 21:27:35 PDT 2014; root:xnu-2422.110.17~1/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.5
list
应该是同质的)并不意味着这是Python的问题,我们需要为这样的任务建立一个内置功能。 - Azat Ibrakov