Сгладить неправильный список списков
Да, я знаю, что эта тема уже была рассмотрена ( здесь, здесь, здесь, здесь), но, насколько я знаю, все решения, кроме одного, терпят неудачу в таком списке:
L = [[[1, 2, 3], [4, 5]], 6]
Где желаемый результат
[1, 2, 3, 4, 5, 6]
Или, может быть, даже лучше, итератор. Единственное решение, которое я видел, что работает для произвольного вложения, находится в этом вопросе:
def flatten(x):
result = []
for el in x:
if hasattr(el, "__iter__") and not isinstance(el, basestring):
result.extend(flatten(el))
else:
result.append(el)
return result
flatten(L)
Это лучшая модель? Я что-то упустил? Любые проблемы?
53 ответа
Использование функций генератора может немного облегчить чтение вашего примера и, вероятно, повысить производительность.
Python 2
def flatten(l):
for el in l:
if isinstance(el, collections.Iterable) and not isinstance(el, basestring):
for sub in flatten(el):
yield sub
else:
yield el
Я использовал Iterable ABC, добавленный в 2.6.
Python 3
В Python 3 basestring
больше нет, но вы можете использовать кортеж str
а также bytes
чтобы получить тот же эффект там.
yield from
Оператор возвращает элемент из генератора по одному. Этот синтаксис для делегирования субгенератору был добавлен в 3.3
def flatten(l):
for el in l:
if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
yield from flatten(el)
else:
yield el
Мое решение:
def flatten(x):
if isinstance(x, collections.Iterable):
return [a for i in x for a in flatten(i)]
else:
return [x]
Чуть более лаконично, но примерно так же.
Генератор, использующий рекурсию и утку (обновлен для Python 3):
def flatten(L):
for item in L:
try:
yield from flatten(item)
except TypeError:
yield item
list(flatten([[[1, 2, 3], [4, 5]], 6]))
>>>[1, 2, 3, 4, 5, 6]
Вот моя функциональная версия рекурсивного сглаживания, которая обрабатывает как кортежи, так и списки, и позволяет вам добавлять любое сочетание позиционных аргументов. Возвращает генератор, который производит всю последовательность в порядке, arg by arg:
flatten = lambda *n: (e for a in n
for e in (flatten(*a) if isinstance(a, (tuple, list)) else (a,)))
Использование:
l1 = ['a', ['b', ('c', 'd')]]
l2 = [0, 1, (2, 3), [[4, 5, (6, 7, (8,), [9]), 10]], (11,)]
print list(flatten(l1, -2, -1, l2))
['a', 'b', 'c', 'd', -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Генераторная версия нерекурсивного решения @unutbu, запрошенная @Andrew в комментарии:
def genflat(l, ltypes=collections.Sequence):
l = list(l)
i = 0
while i < len(l):
while isinstance(l[i], ltypes):
if not l[i]:
l.pop(i)
i -= 1
break
else:
l[i:i + 1] = l[i]
yield l[i]
i += 1
Немного упрощенная версия этого генератора:
def genflat(l, ltypes=collections.Sequence):
l = list(l)
while l:
while l and isinstance(l[0], ltypes):
l[0:1] = l[0]
if l: yield l.pop(0)
Эта версия flatten
избегает предела рекурсии python (и, следовательно, работает с произвольно глубокими вложенными итерациями). Это генератор, который может обрабатывать строки и произвольные итерации (даже бесконечные).
import itertools as IT
import collections
def flatten(iterable, ltypes=collections.Iterable):
remainder = iter(iterable)
while True:
first = next(remainder)
if isinstance(first, ltypes) and not isinstance(first, basestring):
remainder = IT.chain(first, remainder)
else:
yield first
Вот несколько примеров, демонстрирующих его использование:
print(list(IT.islice(flatten(IT.repeat(1)),10)))
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
print(list(IT.islice(flatten(IT.chain(IT.repeat(2,3),
{10,20,30},
'foo bar'.split(),
IT.repeat(1),)),10)))
# [2, 2, 2, 10, 20, 30, 'foo', 'bar', 1, 1]
print(list(flatten([[1,2,[3,4]]])))
# [1, 2, 3, 4]
seq = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))
print(list(flatten(seq)))
# ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', 'g', 'G', 'h', 'H',
# 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', 'o', 'O', 'p', 'P',
# 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', 'W', 'x', 'X',
# 'y', 'Y', 'z', 'Z', 0, 1, 2, 3, 4, 5, 6, 7, 8]
Хотя flatten
может обрабатывать бесконечные генераторы, не может обрабатывать бесконечные вложения:
def infinitely_nested():
while True:
yield IT.chain(infinitely_nested(), IT.repeat(1))
print(list(IT.islice(flatten(infinitely_nested()), 10)))
# hangs
def flatten(xs):
res = []
def loop(ys):
for i in ys:
if isinstance(i, list):
loop(i)
else:
res.append(i)
loop(xs)
return res
У Pandas есть функция, которая это делает. Он возвращает итератор, как вы упомянули.
In [1]: import pandas
In [2]: pandas.core.common.flatten([[[1, 2, 3], [4, 5]], 6])
Out[2]: <generator object flatten at 0x7f12ade66200>
In [3]: list(pandas.core.common.flatten([[[1, 2, 3], [4, 5]], 6]))
Out[3]: [1, 2, 3, 4, 5, 6]
Вот еще один ответ, который еще интереснее...
import re
def Flatten(TheList):
a = str(TheList)
b,crap = re.subn(r'[\[,\]]', ' ', a)
c = b.split()
d = [int(x) for x in c]
return(d)
По сути, он преобразует вложенный список в строку, использует регулярное выражение для удаления вложенного синтаксиса, а затем преобразует результат обратно в (уплощенный) список.
Вы могли бы использовать deepflatten
из стороннего пакета iteration_utilities
:
>>> from iteration_utilities import deepflatten
>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(deepflatten(L))
[1, 2, 3, 4, 5, 6]
>>> list(deepflatten(L, types=list)) # only flatten "inner" lists
[1, 2, 3, 4, 5, 6]
Это итератор, поэтому вам нужно повторить его (например, обернув его list
или используя его в цикле). Внутренне он использует итеративный подход вместо рекурсивного подхода и записывается как расширение C, поэтому он может быть быстрее, чем подходы чистого Python:
>>> %timeit list(deepflatten(L))
12.6 µs ± 298 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit list(deepflatten(L, types=list))
8.7 µs ± 139 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit list(flatten(L)) # Cristian - Python 3.x approach from https://stackru.com/a/2158532/5393381
86.4 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit list(flatten(L)) # Josh Lee - https://stackru.com/a/2158522/5393381
107 µs ± 2.99 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit list(genflat(L, list)) # Alex Martelli - https://stackru.com/a/2159079/5393381
23.1 µs ± 710 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Я автор iteration_utilities
библиотека.
Было забавно пытаться создать функцию, которая могла бы сгладить нерегулярный список в Python, но, конечно, для этого и нужен Python (чтобы программирование было увлекательным). Следующий генератор работает довольно хорошо с некоторыми оговорками:
def flatten(iterable):
try:
for item in iterable:
yield from flatten(item)
except TypeError:
yield iterable
Это сгладит типы данных, которые вы можете оставить в покое (например, bytearray
, bytes
, а также str
объекты). Кроме того, код опирается на тот факт, что запрос итератора из неповторяемого вызывает TypeError
,
>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> def flatten(iterable):
try:
for item in iterable:
yield from flatten(item)
except TypeError:
yield iterable
>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>>
Редактировать:
Я не согласен с предыдущей реализацией. Проблема в том, что вы не должны быть в состоянии сгладить то, что не повторяется. Это сбивает с толку и дает неправильное впечатление от аргумента.
>>> list(flatten(123))
[123]
>>>
Следующий генератор почти такой же, как и первый, но у него нет проблемы с попыткой сгладить не повторяемый объект. Он терпит неудачу, как и следовало ожидать, когда ему дается неуместный аргумент.
def flatten(iterable):
for item in iterable:
try:
yield from flatten(item)
except TypeError:
yield item
Тестирование генератора прекрасно работает с предоставленным списком. Тем не менее, новый код поднимет TypeError
когда ему дается не повторяемый объект. Ниже показан пример нового поведения.
>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>> list(flatten(123))
Traceback (most recent call last):
File "<pyshell#32>", line 1, in <module>
list(flatten(123))
File "<pyshell#27>", line 2, in flatten
for item in iterable:
TypeError: 'int' object is not iterable
>>>
Вот простая функция, которая выравнивает списки произвольной глубины. Нет рекурсии, чтобы избежать переполнения стека.
from copy import deepcopy
def flatten_list(nested_list):
"""Flatten an arbitrarily nested list, without recursion (to avoid
stack overflows). Returns a new list, the original list is unchanged.
>> list(flatten_list([1, 2, 3, [4], [], [[[[[[[[[5]]]]]]]]]]))
[1, 2, 3, 4, 5]
>> list(flatten_list([[1, 2], 3]))
[1, 2, 3]
"""
nested_list = deepcopy(nested_list)
while nested_list:
sublist = nested_list.pop(0)
if isinstance(sublist, list):
nested_list = sublist + nested_list
else:
yield sublist
При попытке ответить на такой вопрос вам действительно нужно указать ограничения кода, который вы предлагаете в качестве решения. Если бы речь шла только о выступлениях, я бы не возражал, но большинство кодов, предложенных в качестве решения (включая принятый ответ), не сгладили ни один список, глубина которого превышает 1000.
Когда я говорю большинство кодов, я имею в виду все коды, которые используют любую форму рекурсии (или вызывают стандартную библиотечную функцию, которая является рекурсивной). Все эти коды не выполняются, потому что для каждого выполненного рекурсивного вызова стек (вызова) увеличивается на одну единицу, а стек вызовов (по умолчанию) python имеет размер 1000.
Если вы не слишком знакомы со стеком вызовов, то, возможно, вам поможет следующее (в противном случае вы можете просто перейти к реализации).
Размер стека вызовов и рекурсивное программирование (аналогия подземелий)
Найти клад и выйти
Представьте, что вы входите в огромное подземелье с пронумерованными комнатами в поисках сокровищ. Вы не знаете место, но у вас есть некоторые указания о том, как найти клад. Каждое указание - загадка (сложность варьируется, но вы не можете предсказать, насколько сложными они будут). Вы решаете немного подумать о стратегии экономии времени, вы делаете два замечания:
- Трудно (долго) найти сокровище, так как вам придется разгадывать (потенциально сложные) загадки, чтобы добраться туда.
- Когда найденное сокровище вернется ко входу, может быть легко, вам просто нужно использовать тот же путь в другом направлении (хотя для того, чтобы вспомнить ваш путь, потребуется немного памяти).
При входе в темницу вы замечаете небольшую тетрадь здесь. Вы решаете использовать его, чтобы записать каждую комнату, в которую вы выходите после решения загадки (при входе в новую комнату), таким образом вы сможете вернуться обратно к входу. Это гениальная идея, вы даже не будете тратить ни цента на реализацию своей стратегии.
Вы входите в темницу, с большим успехом решая первые 1001 загадку, но тут появляется то, что вы не планировали, у вас нет места в заимствованной вами записной книжке. Вы решаете отказаться от своего квеста, так как предпочитаете не иметь сокровища, а быть потерянным навсегда в подземелье (это выглядит действительно умно).
Выполнение рекурсивной программы
По сути, это то же самое, что найти клад. Подземелье - это память компьютера. Ваша цель теперь не в том, чтобы найти сокровище, а в том, чтобы вычислить какую-то функцию (найти f(x) для заданного x). Признаки просто подпрограммы, которые помогут вам решить f(x). Ваша стратегия аналогична стратегии стека вызовов, ноутбук - это стек, комнаты - это адреса возврата функций:
x = ["over here", "am", "I"]
y = sorted(x) # You're about to enter a room named `sorted`, note down the current room address here so you can return back: 0x4004f4 (that room address looks weird)
# Seems like you went back from your quest using the return address 0x4004f4
# Let's see what you've collected
print(' '.join(y))
Проблема, с которой вы столкнулись в подземелье, будет такой же: стек вызовов имеет конечный размер (здесь 1000), и поэтому, если вы введете слишком много функций без возврата назад, вы заполняете стек вызовов и получаете сообщение об ошибке например, "Дорогой искатель приключений, мне очень жаль, но ваш блокнот полон": RecursionError: maximum recursion depth exceeded
, Обратите внимание, что вам не нужна рекурсия для заполнения стека вызовов, но очень маловероятно, чтобы нерекурсивная программа вызывала 1000 функций без какого-либо возврата. Также важно понимать, что после того, как вы вернулись из функции, стек вызовов освобождается от используемого адреса (следовательно, имя "стек", адрес возврата вводятся перед входом в функцию и извлекаются при возврате). В частном случае простой рекурсии (функция f
что звоните себе один раз - снова и снова - вы войдете f
снова и снова, пока вычисление не закончится (пока не будет найдено сокровище) и вернется из f
пока вы не вернетесь к месту, где вы звонили f
на первом месте. Стек вызовов никогда не будет освобожден ни от чего до конца, где он будет освобожден от всех адресов возврата один за другим.
Как избежать этой проблемы?
Это на самом деле довольно просто: "не используйте рекурсию, если вы не знаете, как глубоко она может зайти". Это не всегда так, поскольку в некоторых случаях рекурсия Tail Call может быть оптимизирована (TCO). Но в python это не так, и даже "хорошо написанная" рекурсивная функция не оптимизирует использование стека. Есть интересный пост от Гвидо по этому вопросу: устранение рекурсии хвоста.
Есть методика, которую вы можете использовать, чтобы сделать любую рекурсивную функцию итеративной, этот метод, который мы могли бы назвать, принесет ваш собственный блокнот. Например, в нашем конкретном случае мы просто изучаем список, вход в комнату эквивалентен вводу подсписка, вопрос, который вы должны задать себе: как мне вернуться из списка в его родительский список? Ответ не так сложен, повторяйте следующее до stack
пустой:
- нажмите текущий список
address
а такжеindex
вstack
при вводе нового подсписка (обратите внимание, что адрес списка + индекс также является адресом, поэтому мы просто используем точно такой же метод, который используется стеком вызовов); - каждый раз, когда предмет найден,
yield
это (или добавить их в список); - как только список полностью изучен, вернитесь к родительскому списку, используя
stack
вернутьaddress
(а такжеindex
)
Также обратите внимание, что это эквивалентно DFS в дереве, где некоторые узлы являются подсписками A = [1, 2]
и некоторые простые вещи: 0, 1, 2, 3, 4
(за L = [0, [1,2], 3, 4]
). Дерево выглядит так:
L
|
-------------------
| | | |
0 --A-- 3 4
| |
1 2
Предварительный порядок обхода DFS: L, 0, A, 1, 2, 3, 4. Помните, что для реализации итеративной DFS вам также "нужен" стек. Реализация, которую я предложил ранее, привела к следующим состояниям (для stack
и flat_list
):
init.: stack=[(L, 0)]
**0**: stack=[(L, 0)], flat_list=[0]
**A**: stack=[(L, 1), (A, 0)], flat_list=[0]
**1**: stack=[(L, 1), (A, 0)], flat_list=[0, 1]
**2**: stack=[(L, 1), (A, 1)], flat_list=[0, 1, 2]
**3**: stack=[(L, 2)], flat_list=[0, 1, 2, 3]
**3**: stack=[(L, 3)], flat_list=[0, 1, 2, 3, 4]
return: stack=[], flat_list=[0, 1, 2, 3, 4]
В этом примере максимальный размер стека равен 2, поскольку входной список (и, следовательно, дерево) имеют глубину 2.
Реализация
Для реализации в python вы можете немного упростить использование итераторов вместо простых списков. Ссылки на (под) итераторы будут использоваться для хранения адресов возврата подсписков (вместо того, чтобы иметь как адрес списка, так и индекс). Это не большая разница, но я чувствую, что это более читабельно (а также немного быстрее):
def flatten(iterable):
return list(items_from(iterable))
def items_from(iterable):
cursor_stack = [iter(iterable)]
while cursor_stack:
sub_iterable = cursor_stack[-1]
try:
item = next(sub_iterable)
except StopIteration: # post-order
cursor_stack.pop()
continue
if is_list_like(item): # pre-order
cursor_stack.append(iter(item))
elif item is not None:
yield item # in-order
def is_list_like(item):
return isinstance(item, list)
Кроме того, обратите внимание, что в is_list_like
я имею isinstance(item, list)
, который может быть изменен для обработки большего количества типов ввода, здесь я просто хотел иметь простейшую версию, где (итерируемый) - это просто список. Но вы также можете сделать это:
def is_list_like(item):
try:
iter(item)
return not isinstance(item, str) # strings are not lists (hmm...)
except TypeError:
return False
Это рассматривает строки как "простые элементы" и, следовательно, flatten_iter([["test", "a"], "b])
вернусь ["test", "a", "b"]
и не ["t", "e", "s", "t", "a", "b"]
, Обратите внимание, что в этом случае, iter(item)
вызывается дважды для каждого элемента, давайте представим, что это упражнение для читателя, чтобы сделать это чище.
Тестирование и замечания по другим реализациям
В конце помните, что вы не можете напечатать бесконечно вложенный список L
с помощью print(L)
потому что внутри он будет использовать рекурсивные вызовы __repr__
(RecursionError: maximum recursion depth exceeded while getting the repr of an object
). По той же причине, решения flatten
с участием str
потерпит неудачу с тем же сообщением об ошибке.
Если вам нужно протестировать свое решение, вы можете использовать эту функцию для генерации простого вложенного списка:
def build_deep_list(depth):
"""Returns a list of the form $l_{depth} = [depth-1, l_{depth-1}]$
with $depth > 1$ and $l_0 = [0]$.
"""
sub_list = [0]
for d in range(1, depth):
sub_list = [d, sub_list]
return sub_list
Который дает: build_deep_list(5)
>>> [4, [3, [2, [1, [0]]]]]
,
Хотя был выбран элегантный и очень питонический ответ, я бы представил свое решение только для обзора:
def flat(l):
ret = []
for i in l:
if isinstance(i, list) or isinstance(i, tuple):
ret.extend(flat(i))
else:
ret.append(i)
return ret
Скажите, пожалуйста, насколько хорош или плох этот код?
Я не просмотрел все уже имеющиеся ответы здесь, но вот один вкладыш, который я придумал, заимствуя способ lisp обработки первого и остальных списков
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]
>>>
Я предпочитаю простые ответы. Нет генераторов. Нет рекурсии или рекурсивных ограничений. Просто итерация:
def flatten(TheList):
listIsNested = True
while listIsNested: #outer loop
keepChecking = False
Temp = []
for element in TheList: #inner loop
if isinstance(element,list):
Temp.extend(element)
keepChecking = True
else:
Temp.append(element)
listIsNested = keepChecking #determine if outer loop exits
TheList = Temp[:]
return TheList
Это работает с двумя списками: внутренний цикл for и внешний цикл while.
Внутренний цикл for просматривает список. Если он находит элемент списка, он (1) использует list.extend() для выравнивания этой части первого уровня вложенности и (2) переключает keepChecking в True. keepchecking используется для управления внешним циклом while. Если внешний цикл установлен в значение true, он запускает внутренний цикл для следующего прохода.
Эти проходы продолжают происходить до тех пор, пока не будут найдены вложенные списки. Когда, наконец, происходит проход, где ничего не найдено, keepChecking никогда не получает значение true, что означает, что listIsNested остается false, а внешний цикл while завершается.
Сглаженный список затем возвращается.
Тестовый забег
flatten([1,2,3,4,[100,200,300,[1000,2000,3000]]])
[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]
Просто используйте funcy
библиотека: pip install funcy
import funcy
funcy.flatten([[[[1, 1], 1], 2], 3]) # returns generator
funcy.lflatten([[[[1, 1], 1], 2], 3]) # returns list
Вот compiler.ast.flatten
реализация в 2.7.5:
def flatten(seq):
l = []
for elt in seq:
t = type(elt)
if t is tuple or t is list:
for elt2 in flatten(elt):
l.append(elt2)
else:
l.append(elt)
return l
Есть лучшие, более быстрые методы (если вы достигли здесь, вы уже видели их)
Также обратите внимание:
Устаревший с версии 2.6: пакет компилятора был удален в Python 3.
Я удивлен, что никто не подумал об этом. Чертова рекурсия Я не получаю рекурсивных ответов, которые сделали продвинутые люди здесь. во всяком случае, вот моя попытка на этом. предостережение это очень специфично для варианта использования ОП
import re
L = [[[1, 2, 3], [4, 5]], 6]
flattened_list = re.sub("[\[\]]", "", str(L)).replace(" ", "").split(",")
new_list = list(map(int, flattened_list))
print(new_list)
выход:
[1, 2, 3, 4, 5, 6]
Я не уверен, если это обязательно быстрее или эффективнее, но это то, что я делаю:
def flatten(lst):
return eval('[' + str(lst).replace('[', '').replace(']', '') + ']')
L = [[[1, 2, 3], [4, 5]], 6]
print(flatten(L))
flatten
Функция здесь превращает список в строку, вынимает все квадратные скобки, прикрепляет квадратные скобки обратно к концам и превращает его обратно в список.
Хотя, если бы вы знали, что в вашем списке будут квадратные скобки в виде строк, например [[1, 2], "[3, 4] and [5]"]
, вам придется сделать что-то еще.
Я знаю, что уже есть много удивительных ответов, но я хотел добавить ответ, который использует метод функционального программирования для решения вопроса. В этом ответе я использую двойную рекурсию:
def flatten_list(seq):
if not seq:
return []
elif isinstance(seq[0],list):
return (flatten_list(seq[0])+flatten_list(seq[1:]))
else:
return [seq[0]]+flatten_list(seq[1:])
print(flatten_list([1,2,[3,[4],5],[6,7]]))
выход:
[1, 2, 3, 4, 5, 6, 7]
Нет рекурсии или вложенных циклов. Несколько строк. Хорошо отформатирован и легко читается:
def flatten_deep(arr: list):
""" Flattens arbitrarily-nested list `arr` into single-dimensional. """
while arr:
if isinstance(arr[0], list): # Checks whether first element is a list
arr = arr[0] + arr[1:] # If so, flattens that first element one level
else:
yield arr.pop(0) # Otherwise yield as part of the flat array
flatten_deep(L)
Из моего собственного кода на https://github.com/jorgeorpinel/flatten_nested_lists/blob/master/flatten.py
Это может быть старый вопрос, который я хотел бы попробовать.
Я тупой парень, поэтому я дам "тупое" решение. вся эта рекурсия вредит моему мозгу.
flattened_list = []
nested_list =[[[1, 2, 3], [4, 5]], 6]
def flatten(nested_list, container):
for item in nested_list:
if isintance(item, list):
flatten(item)
else:
container.append(i)
>>> flatten(nested_list, flattened_list)
>>> flattened_list
[1, 2, 3, 4, 5, 6]
Я понимаю, что он использует побочный эффект, но хорошо, что в меру моего понимания рекурсии может пойти
Полностью взломанный, но я думаю, что это будет работать (в зависимости от вашего data_type)
flat_list = ast.literal_eval("[%s]"%re.sub("[\[\]]","",str(the_list)))
Без использования какой-либо библиотеки:
def flat(l):
def _flat(l, r):
if type(l) is not list:
r.append(l)
else:
for i in l:
r = r + flat(i)
return r
return _flat(l, [])
# example
test = [[1], [[2]], [3], [['a','b','c'] , [['z','x','y']], ['d','f','g']], 4]
print flat(test) # prints [1, 2, 3, 'a', 'b', 'c', 'z', 'x', 'y', 'd', 'f', 'g', 4]
Никакие оборки. Только озноб.
recursive_list_of_lists = [1,2,3,[1,2,[[3,4,[5]],7,0,1,10],100,[101,[101,[[101]],2]],0]]
k = []
def flatten(subl):
for i in subl:
if type(i) != type([1]):
k.append(i)
else:
flatten(i)
flatten(recursive_list_of_lists)
print(k)
[1, 2, 3, 1, 2, 3, 4, 5, 7, 0, 1, 10, 100, 101, 101, 101, 2, 0]
Это простая реализация flatten на python2
flatten=lambda l: reduce(lambda x,y:x+y,map(flatten,l),[]) if isinstance(l,list) else [l]
test=[[1,2,3,[3,4,5],[6,7,[8,9,[10,[11,[12,13,14]]]]]],]
print flatten(test)
#output [1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
Самый простой способ - использовать библиотеку morph, используя pip install morph
,
Код является:
import morph
list = [[[1, 2, 3], [4, 5]], 6]
flattened_list = morph.flatten(list) # returns [1, 2, 3, 4, 5, 6]
С помощью itertools.chain
:
import itertools
from collections import Iterable
def list_flatten(lst):
flat_lst = []
for item in itertools.chain(lst):
if isinstance(item, Iterable):
item = list_flatten(item)
flat_lst.extend(item)
else:
flat_lst.append(item)
return flat_lst
Или без цепочки:
def flatten(q, final):
if not q:
return
if isinstance(q, list):
if not isinstance(q[0], list):
final.append(q[0])
else:
flatten(q[0], final)
flatten(q[1:], final)
else:
final.append(q)
Мы также можем использовать функцию "type" в python. При переборе списка мы проверяем, является ли элемент списком или нет. Если нет, мы "добавляем" его, иначе "расширяем". Вот пример кода -
l=[1,2,[3,4],5,[6,7,8]]
x=[]
for i in l:
if type(i) is list:
x.extend(i)
else:
x.append(i)
print x
Выход:
[1, 2, 3, 4, 5, 6, 7, 8]
Для получения дополнительной информации о append() и extend() посетите этот веб-сайт: https://docs.python.org/2/tutorial/datastructures.html