Вот еще один подход 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))
Он может игнорировать любой определенный (или производный) тип, который вам нужен, он возвращает итератор, поэтому вы можете преобразовать его в любой конкретный контейнер, такой как list, tuple, dict или просто использовать его, чтобы уменьшить объем памяти, для лучше или хуже, он может обрабатывать исходные не повторяемые объекты, такие как int ...
Обратите внимание, что большая часть тяжелой работы выполняется в C, поскольку, насколько мне известно, именно так реализованы itertools, поэтому, несмотря на то, что он рекурсивный, AFAIK не ограничен глубиной рекурсии 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