Да, я знаю, что эта тема уже была рассмотрена ( здесь , здесь , здесь , здесь ), но, насколько я знаю, все решения, кроме одного, терпят неудачу в таком списке:
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)
Это лучшая модель? Я что-то упустил? Любые проблемы?
python
list
optimization
nested-lists
flatten
telliott99
источник
источник
list
предназначенное для того, чтобы быть однородными), не означает, что это ошибка Python, и нам нужна встроеннаяОтветы:
Использование функций генератора может немного облегчить чтение вашего примера и, вероятно, повысить производительность.
Python 2
Я использовал Iterable ABC, добавленный в 2.6.
Python 3
В Python 3 этого
basestring
больше нет, но вы можете использовать кортежstr
и,bytes
чтобы получить тот же эффект.yield from
Оператор возвращает элемент из генератора по одному. Этот синтаксис для делегирования субгенератору был добавлен в 3.3источник
l = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))
когда я сделал этоlist(flatten(l))
. Все остальные, начинали работать и брали навсегда!collections.Sequence
вместоcollections.Iteratable
?for i in flatten(42): print (i)
. Это можно исправить, переместивisinstance
-test и условие else за пределамиfor el
-loop. (Тогда вы могли бы бросить что-нибудь в это, и это сделало бы сплющенный список из этого)collections.Iterable
устарело. Используйтеcollections.abc.Iterable
вместо этого.Мое решение:
Чуть более лаконично, но примерно так же.
источник
try: iter(x)
протестируете, является ли это итеративным ... Но я не думаю, что необходимость импортировать модуль stdlib - это недостаток, которого стоит избегать.int
def flatten(x): return [a for i in x for a in flatten(i)] if isinstance(x, collections.Iterable) else [x]
но читабельность здесь может быть субъективной.if isinstance(x, collections.Iterable) and not isinstance(x, basestring)
collections.Iterable
наlist
Генератор, использующий рекурсию и утку (обновлен для Python 3):
источник
for i in flatten(item): yield i
Генераторная версия нерекурсивного решения @ unutbu, запрошенная @Andrew в комментарии:
Немного упрощенная версия этого генератора:
источник
Вот моя функциональная версия рекурсивного сглаживания, которая обрабатывает как кортежи, так и списки, и позволяет вам добавлять любое сочетание позиционных аргументов. Возвращает генератор, который производит всю последовательность в порядке, arg by arg:
Применение:
источник
e
,a
,n
смargs
дляn
,intermediate
(или короче ,mid
или вы можете предпочестьelement
) дляa
иresult
дляe
, так:flatten = lambda *args: (result for mid in args for result in (flatten(*mid) if isinstance(mid, (tuple, list)) else (mid,)))
compiler.ast.flatten
. Отличный, компактный код, работает для любого (я думаю) типа объекта.Эта версия
flatten
избегает предела рекурсии Python (и, следовательно, работает с произвольно глубокими вложенными итерациями). Это генератор, который может обрабатывать строки и произвольные итерации (даже бесконечные).Вот несколько примеров, демонстрирующих его использование:
Хотя он
flatten
может обрабатывать бесконечные генераторы, он не может обрабатывать бесконечное вложение:источник
sets
,dicts
,deques
,listiterators
,generators
, Дескрипторы файлов, а также пользовательские классы с__iter__
определены все случаиcollections.Iterable
, но неcollections.Sequence
. Результат сглаживания adict
немного ненадежен, но в остальном, я думаю,collections.Iterable
лучше, чем по умолчаниюcollections.Sequence
. Это определенно более либерально.collections.Iterable
заключается в том, что это включает в себя бесконечные генераторы. Я изменил свой ответ справиться с этим делом.StopIteration
. Кроме того, похоже,while True: first = next(remainder)
может быть замененоfor first in remainder:
.try-except StopIteration block
.Вот еще один ответ, который еще интереснее ...
По сути, он преобразует вложенный список в строку, использует регулярное выражение для удаления вложенного синтаксиса, а затем преобразует результат обратно в (уплощенный) список.
источник
[['C=64', 'APPLE ]['], ['Amiga', 'Mac', 'ST']]
:). С другой стороны, учитывая список, который содержит себя, это будет немного лучше, чем другие ответы, поднимая исключение вместо того, чтобы просто зацикливаться до тех пор, пока у вас не закончатся память / рекурсивно, пока вы не исчерпаете стек ...[x for x in c]
это медленный и многословный способ сделать копиюc
, так почему бы вам это сделать? Во-вторых, ваш код явно будет преобразован'APPLE ]['
в'APPLE '
, потому что он не обрабатывает кавычки, он просто предполагает, что любые скобки являются скобками списка.arr_str = str(arr)
а потом[int(s) for s in re.findall(r'\d+', arr_str)]
действительно. См. Github.com/jorgeorpinel/flatten_nested_lists/blob/master/…источник
Вы можете использовать
deepflatten
из стороннего пакетаiteration_utilities
:Это итератор, поэтому вам нужно выполнить итерацию (например, обернуть его
list
или использовать в цикле). Внутренне он использует итеративный подход вместо рекурсивного подхода и написан как расширение C, поэтому он может быть быстрее, чем подходы чистого Python:Я автор
iteration_utilities
библиотеки.источник
Было забавно пытаться создать функцию, которая могла бы сгладить нерегулярный список в Python, но, конечно, для этого и нужен Python (чтобы программирование было увлекательным). Следующий генератор работает довольно хорошо с некоторыми оговорками:
Это будет выравниваться типы данных , которые вы можете оставить в покое (как
bytearray
,bytes
иstr
объекты). Кроме того, код опирается на тот факт, что запрос итератора из неповторяемого вызывает aTypeError
.Редактировать:
Я не согласен с предыдущей реализацией. Проблема в том, что вы не должны быть в состоянии сгладить то, что не повторяется. Это сбивает с толку и дает неправильное впечатление от аргумента.
Следующий генератор почти такой же, как первый, но у него нет проблемы с попыткой выравнивания не повторяемого объекта. Он терпит неудачу, как и следовало ожидать, когда ему дается неуместный аргумент.
Тестирование генератора прекрасно работает с предоставленным списком. Тем не менее, новый код будет вызывать,
TypeError
когда ему дается не повторяемый объект. Ниже приведен пример нового поведения.источник
Хотя был выбран элегантный и очень питонический ответ, я бы представил свое решение только для обзора:
Скажите, пожалуйста, насколько хорош или плох этот код?
источник
isinstance(i, (tuple, list))
. Инициализация пустых переменных - это флаг для меня, чтобы посмотреть на альтернативные структуры кода, как правило, на понимание, генераторы, рекурсию и т. Д.return type(l)(ret)
вернет вам тот же тип контейнера, который был передан в. :)Я предпочитаю простые ответы. Нет генераторов. Нет рекурсии или рекурсивных ограничений. Просто итерация:
Это работает с двумя списками: внутренний цикл for и внешний цикл while.
Внутренний цикл for просматривает список. Если он находит элемент списка, он (1) использует list.extend (), чтобы сгладить эту часть первого уровня вложенности, и (2) переключает keepChecking в True. keepchecking используется для управления внешним циклом while. Если внешний цикл установлен в значение true, он запускает внутренний цикл для другого прохода.
Эти проходы продолжаются до тех пор, пока не будут найдены вложенные списки. Когда, наконец, происходит проход, где ничего не найдено, keepChecking никогда не получает значение true, что означает, что listIsNested остается false, а внешний цикл while завершается.
Выровненный список затем возвращается.
Тестовый забег
[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]
источник
Вот простая функция, которая выравнивает списки произвольной глубины. Нет рекурсии, чтобы избежать переполнения стека.
источник
Я удивлен, что никто не подумал об этом. Чертова рекурсия Я не получаю рекурсивных ответов, которые сделали продвинутые люди здесь. в любом случае, вот моя попытка на это. предостережение это очень специфично для варианта использования ОП
вывод:
источник
Я не просмотрел все уже имеющиеся ответы здесь, но вот один вкладыш, который я придумал, заимствуя способ lisp обработки первого и остальных списков
Вот один простой и один не очень простой случай -
источник
def foo():
это отдельная линия. Кроме того, это очень нечитаемо.При попытке ответить на такой вопрос вам действительно нужно указать ограничения кода, который вы предлагаете в качестве решения. Если бы речь шла только о производительности, я бы не возражал, но большинство кодов, предложенных в качестве решения (включая принятый ответ), не могут сгладить ни один список, глубина которого превышает 1000.
Когда я говорю большинство кодов я имею в виду все коды, которые используют любую форму рекурсии (или вызывают стандартную библиотечную функцию, которая является рекурсивной). Все эти коды не выполняются, потому что для каждого сделанного рекурсивного вызова стек (вызова) увеличивается на одну единицу, а стек вызовов (по умолчанию) python имеет размер 1000.
Если вы не слишком знакомы со стеком вызовов, возможно, вам поможет следующее (в противном случае вы можете просто прокрутить до реализации ).
Размер стека вызовов и рекурсивное программирование (аналогия подземелий)
Найти клад и выйти
Представьте, что вы входите в огромное подземелье с пронумерованными комнатами в поисках сокровищ. Вы не знаете место, но у вас есть некоторые указания о том, как найти клад. Каждое указание - загадка (сложность варьируется, но вы не можете предсказать, насколько сложными они будут). Вы решаете немного подумать о стратегии экономии времени, вы делаете два замечания:
При входе в темницу вы видите небольшую тетрадь здесь. Вы решаете использовать его, чтобы записать каждую комнату, в которую вы выходите после решения загадки (при входе в новую комнату), таким образом, вы сможете вернуться обратно ко входу. Это гениальная идея, вы даже не будете тратить ни цента реализацию своей стратегии.
Вы входите в темницу, с большим успехом разгадывая первые 1001 загадку, но тут появляется то, что вы не планировали, у вас нет места в заимствованной вами записной книжке. Вы решаете отказаться от своего квеста, так как вы предпочитаете не иметь сокровища, а быть потерянным навсегда в подземелье (это выглядит действительно умно).
Выполнение рекурсивной программы
По сути, это то же самое, что найти клад. Подземелье - это память компьютера. Ваша цель теперь не в том, чтобы найти сокровище, а в том, чтобы вычислить некоторую функцию (найти f (x) для заданного x ). Показания просто подпрограммы, которые помогут вам решить f (x) . Ваша стратегия аналогична стратегии стека вызовов , ноутбук - это стек, комнаты - адреса возврата функций:
Проблема, с которой вы столкнулись в подземелье, будет такой же: стек вызовов имеет конечный размер (здесь 1000), и поэтому, если вы введете слишком много функций без возврата назад, вы заполняете стек вызовов и получаете сообщение об ошибке например,
«Дорогой искатель приключений, мне очень жаль, но ваша записная книжка заполнена»,: которая вызывает себя один раз - снова и снова - вы будете входить снова и снова, пока не закончится вычисление (пока не будет найдено сокровище) и вернетесь с до Вы возвращаетесь к тому месту, где вы в первую очередь звонили . Стек вызовов никогда не будет освобожден ни от чего до конца, где он будет освобожден от всех адресов возврата один за другим.RecursionError: maximum recursion depth exceeded
. Обратите внимание, что вам не нужна рекурсия для заполнения стека вызовов, но очень маловероятно, чтобы нерекурсивная программа вызывала 1000 функций без какого-либо возврата. Также важно понимать, что после того, как вы вернулись из функции, стек вызовов освобождается от используемого адреса (следовательно, имя «стек», адрес возврата вставляется перед входом в функцию и извлекается при возврате). В частном случае простой рекурсии (функцияf
f
f
f
Как избежать этой проблемы?
Это на самом деле довольно просто: «не используйте рекурсию, если вы не знаете, как глубоко она может зайти». Это не всегда так, поскольку в некоторых случаях рекурсия Tail Call может быть оптимизирована (TCO) . Но в python это не так, и даже «хорошо написанная» рекурсивная функция не оптимизирует использование стека. Есть интересный пост от Гвидо по этому вопросу: устранение рекурсии хвоста .
Есть методика, которую вы можете использовать, чтобы сделать любую рекурсивную функцию итеративной, этот метод, который мы могли бы назвать, принесет ваш собственный блокнот . Например, в нашем конкретном случае мы просто изучаем список, вход в комнату эквивалентен вводу подсписка, вопрос, который вы должны задать себе, - как я могу вернуться из списка в его родительский список? Ответ не так уж сложен, повторяйте следующее до тех пор, пока поле не
stack
станет пустым:address
иindex
вstack
при входе в новый подсписок (обратите внимание, что адрес списка + индекс также является адресом, поэтому мы просто используем точно такой же метод, который используется стеком вызовов);yield
он (или добавить их в список);stack
returnaddress
(иindex
) .Также обратите внимание, что это эквивалентно DFS в дереве, где некоторые узлы являются подсписками,
A = [1, 2]
а некоторые - простыми элементами:0, 1, 2, 3, 4
(дляL = [0, [1,2], 3, 4]
). Дерево выглядит так:Предварительный порядок обхода DFS: L, 0, A, 1, 2, 3, 4. Помните, что для реализации итеративной DFS вам также «нужен» стек. Реализация, которую я предложил ранее, привела к следующим состояниям (для
stack
иflat_list
):В этом примере максимальный размер стека равен 2, поскольку входной список (и, следовательно, дерево) имеют глубину 2.
Реализация
Для реализации в Python вы можете немного упростить использование итераторов вместо простых списков. Ссылки на (под) итераторы будут использоваться для хранения адресов возврата подсписков (вместо того, чтобы иметь адрес списка и индекс). Это не большая разница, но я чувствую, что это более читабельно (а также немного быстрее):
Кроме того, обратите внимание, что у
is_list_like
меня естьisinstance(item, list)
, который может быть изменен для обработки большего количества типов ввода, здесь я просто хотел иметь простейшую версию, где (итерируемый) это просто список. Но вы также можете сделать это:Это рассматривает строки как "простые элементы" и поэтому
flatten_iter([["test", "a"], "b])
будет возвращаться,["test", "a", "b"]
а не возвращаться["t", "e", "s", "t", "a", "b"]
. Обратите внимание, что в этом случаеiter(item)
вызывается дважды для каждого элемента, давайте представим, что это упражнение для читателя, чтобы сделать это чище.Тестирование и замечания по другим реализациям
В конце помните, что вы не можете распечатать бесконечно вложенный список,
L
используя,print(L)
потому что внутренне он будет использовать рекурсивные вызовы to__repr__
(RecursionError: maximum recursion depth exceeded while getting the repr of an object
). По той же причине, решения дляflatten
вовлеченияstr
потерпят неудачу с тем же сообщением об ошибке.Если вам нужно протестировать свое решение, вы можете использовать эту функцию для генерации простого вложенного списка:
Который дает:
build_deep_list(5)
>>>[4, [3, [2, [1, [0]]]]]
.источник
Вот
compiler.ast.flatten
реализация в 2.7.5:Есть лучшие, более быстрые методы (если вы достигли здесь, вы уже видели их)
Также обратите внимание:
источник
полностью взломанный, но я думаю, что это будет работать (в зависимости от вашего data_type)
источник
Просто используйте
funcy
библиотеку:pip install funcy
источник
Вот еще один подход py2, я не уверен, что это самый быстрый или самый элегантный и самый безопасный ...
Он может игнорировать любой конкретный (или производный) тип, который вам нужен, он возвращает итератор, поэтому вы можете преобразовать его в любой конкретный контейнер, такой как list, tuple, dict или просто использовать его, чтобы уменьшить объем памяти, к лучшему или к худшему он может обрабатывать исходные не повторяемые объекты, такие как int ...
Обратите внимание, что большая часть тяжелой работы выполняется в C, так как, насколько я знаю, именно так реализованы itertools, поэтому, хотя это и рекурсивно, AFAIK не ограничено глубиной рекурсии python, поскольку вызовы функций происходят в C, хотя это не означает, что вы ограничены памятью, особенно в OS X, где размер стека имеет жесткое ограничение на сегодняшний день (OS X Mavericks) ...
есть немного более быстрый подход, но менее переносимый метод, используйте его, только если вы можете предположить, что базовые элементы ввода могут быть явно определены иначе, вы получите бесконечную рекурсию, и OS X с ее ограниченным размером стека, будет бросить ошибку сегментации довольно быстро ...
здесь мы используем наборы, чтобы проверить тип, поэтому требуется O (1) против O (количество типов), чтобы проверить, следует ли игнорировать элемент, хотя, конечно, любое значение с производным типом из указанных типов игнорируется Вот почему его используют
str
,unicode
поэтому используйте его с осторожностью ...Тесты:
источник
Без использования какой-либо библиотеки:
источник
Использование
itertools.chain
:Или без цепочки:
источник
Я использовал рекурсив, чтобы решить вложенный список с любой глубиной
Так что после того, как я определил функцию comb_nlist, легко использовать эту функцию для выравнивания. Или вы можете объединить это в одну функцию. Мне нравится мое решение, потому что оно может быть применено к любому вложенному списку.
результат
источник
current_value = combiner(current_value,each_item) RecursionError: maximum recursion depth exceeded
Самый простой способ - использовать библиотеку morph с помощью
pip install morph
.Код является:
источник
Я знаю, что уже есть много удивительных ответов, но я хотел бы добавить ответ, который использует метод функционального программирования для решения вопроса. В этом ответе я использую двойную рекурсию:
вывод:
источник
Я не уверен, если это обязательно быстрее или эффективнее, но это то, что я делаю:
flatten
Функция здесь превращает список в строку, вынимает все квадратные скобки, придает квадратные скобки назад на концах, и превращает его обратно в список.Хотя, если бы вы знали, что у вас в списке будут квадратные скобки, например
[[1, 2], "[3, 4] and [5]"]
, вам придется заняться чем-то другим.источник
Это простая реализация flatten на python2
источник
Это сгладит список или словарь (или список списков или словарей словарей и т. Д.). Предполагается, что значения являются строками, и создается строка, которая объединяет каждый элемент с аргументом-разделителем. Если вы хотите, вы можете использовать разделитель, чтобы потом разделить результат на объект списка. Он использует рекурсию, если следующим значением является список или строка. Используйте аргумент key, чтобы указать, хотите ли вы получить ключи или значения (установите для ключа значение false) из объекта словаря.
выходы:
источник
Если вам нравится рекурсия, это может быть интересным для вас решением:
Я на самом деле адаптировал это из некоторого практического кода Схемы, который я написал некоторое время назад.
Наслаждайтесь!
источник
Я новичок в Python и пришел с фоном LISP. Вот что я придумал (посмотрите названия вар для lulz):
Кажется, работает. Тестовое задание:
возвращает:
источник