Создание списка плохих списков из списка в Python


1,811

Интересно, есть ли ярлык, чтобы сделать простой список из списка списков в Python.

Я могу сделать это в петле цикла, но, возможно, есть какой-то классный «однострочный»? Я попытался с уменьшить, но я получаю сообщение об ошибке.

Код

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] 
reduce(lambda x, y: x.extend(y), l) 

Сообщение об ошибке

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 1, in <lambda> 
AttributeError: 'NoneType' object has no attribute 'extend' 
+2

Некоторые другие ответы лучше, но причина ваш терпит неудачу в том, что «расширить» метод всегда возвращает None. Для списка с длиной 2 он будет работать, но возвращает None. Для более длинного списка он будет потреблять первые 2 аргумента, которые возвращают None. Затем он продолжается с None.extend (<third arg>), что вызывает это erro. 11 июн. 132013-06-11 21:48:27

+8

. Здесь есть подробное обсуждение этого вопроса: http://rightfootin.blogspot.com/2006/09/more-on-python-flatten.html, обсуждая несколько методов сглаживания произвольно вложенных списков списков. Интересно читать! 04 июн. 092009-06-04 20:41:13

  0

@ shawn-chin solution здесь больше pythonic, но если вам нужно сохранить тип последовательности, скажем, что у вас есть кортеж кортежей, а не список списков, тогда вы должны использовать reduce (operator.concat, tuple_of_tuples). Использование operator.concat с кортежами, по-видимому, выполняется быстрее, чем chain.from_iterables со списком. 06 окт. 142014-10-06 21:46:22

  0

numpy.array ([[1], [2]]). Flatten(). Tolist(), который удаляет внутреннюю структуру и возвращает список [1,2] 10 сен. 172017-09-10 17:37:07

2,628
flat_list = [item for sublist in l for item in sublist] 

, что означает:

for sublist in l: 
    for item in sublist: 
     flat_list.append(item) 

быстрее, чем ярлыки, размещенные до сих пор. (l список выравниваться.)

Вот соответствующая функция:

flatten = lambda l: [item for sublist in l for item in sublist] 

Для доказательства, как всегда, вы можете использовать timeit модуль в стандартной библиотеке:

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]' 
10000 loops, best of 3: 143 usec per loop 
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])' 
1000 loops, best of 3: 969 usec per loop 
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)' 
1000 loops, best of 3: 1.1 msec per loop 

Объяснение: ярлыки на основе + (включая подразумеваемое использование в sum) являются необходимыми O(L**2), когда есть L подписок - поскольку промежуточный список результатов продолжает получать longe r, на каждом шаге выделяется новый объект списка промежуточных результатов, и все элементы предыдущего промежуточного результата должны быть скопированы (а также несколько новых добавленных в конце). Таким образом (для простоты и без фактической потери общности) скажем, что у вас есть L подсписок из I предметов каждый: первые предметы I копируются взад и вперед L-1 раз, второй I - L-2 раза и т. Д .; общее количество копий I умножает сумму x для x от 1 до L, i.e., I * (L**2)/2.

Пояснение списка просто генерирует один список, один раз и копирует каждый элемент (от его первоначального места жительства до списка результатов) также ровно один раз.

+1

Не уверен, что это самое быстрое, но самое читаемое для меня решение, поскольку оно не опиралось ни на что, кроме списков. 04 июн. 092009-06-04 20:41:51

+323

Я попробовал тест с теми же данными, используя 'itertools.chain.from_iterable':' $ python -mtimeit -s'from цепочку импорта itertools; l = [[1,2,3], [4,5,6], [7], [8,9]] * 99 '' list (chain.from_iterable (l)) '. Он работает чуть более, чем в два раза быстрее, чем понимание вложенного списка, которое является самым быстрым из приведенных здесь альтернатив. 15 окт. 102010-10-15 01:21:33

+4

$ python -mtimeit -s'l = [[1,2,3], [4,5,6], [7], [8,9]] * 99 '' b = [] '' extend = b .extend '' для sub в l: '' extend (sub) ' 10000 циклов, наилучший из 3: 36,6 usec за цикл $ python -mtimeit -s'l = [[1,2,3], [ 4,5,6], [7], [8,9]] * 99 '' [item для подсписок в l для элемента в подсписке] ' 10000 циклов, наилучший из 3: 86,4 usec за цикл 16 мар. 112011-03-16 18:47:23

+197

Я нашел синтаксис трудно понять, пока я не понял, что вы можете думать о нем точно так же, как вложенные для циклов. для подписок в l: для элемента в подсписке: элемент доходности 27 июл. 112011-07-27 16:43:18

+4

@ на вашем решении фактически возвращает ** итератор **, а не фактический список - поэтому он работает в два раза быстрее. см. «Тип (itertools.chain ([[1,2], [3,4]]))'. , но если @emma нуждается в вложенном списке, просто чтобы перебрать его - это прекрасное и оптимальное решение: o) 19 май. 122012-05-19 21:59:24

+15

@BorisChervenkov: Обратите внимание, что я завернул вызов в 'list()', чтобы реализовать итератор в списке. 20 май. 122012-05-20 22:56:40

+1

'numpy.concatenate' выглядит немного быстрее, чем любой из методов здесь, если вы готовы принять массив. 19 июл. 122012-07-19 08:04:55

+6

Не работает повсеместно! 'L = [1,2, [3,4]] [пункт для подсписка в литрах для элемента подсписка] TypeError: 'INT' объект не iterable' 27 мар. 132013-03-27 14:00:29

+2

@Noio Это имеет смысл, если вы повторно закажите его: '[item для элемента в подсписке для подсписок в l]'. Конечно, если вы переупорядочите его, то это не будет иметь смысла для * Python *, потому что вы используете 'sublist', прежде чем вы определили, что это такое. 23 май. 132013-05-23 22:29:51

+97

[лист для дерева в лесу для листьев в дереве] может быть проще понять и применить. 29 авг. 132013-08-29 01:38:20

+2

@Sven Он работает для любого списка списков; '[1,2, [3,4]]' не список списков. Вы можете использовать однострочное решение для этого конкретного случая с чем-то вроде: '[item для подписок в [sublist if isinstance (sublist, list) else [sublist] для подписок в l] для элемента в подсписке]'; но это недостаточно кратким, чтобы стоить в одной строке.Альтернативно, достаточно просто написать себе функцию для сглаживания произвольно вложенных последовательностей; см. также http://stackoverflow.com/a/2158532/2359271 03 окт. 132013-10-03 17:51:46

  0

@wim Я действительно просто напишу структуру цикла для цикла в полном объеме, если я не уверен, что делает понимание списка. Хотя я дам, что по номиналу значение 'item для sublist в l для item в sublist' звучит как ерунда. 08 окт. 132013-10-08 13:18:31

+1

пример сокращения в основном тексте использует оператор + и, следовательно, создает список N. 'reduce (lambda x, y: x.extend (y) или x, ll, [])' намного быстрее, чем понимание списка в моем тесте. Сложной частью является выражение: x.extend (y) return None, поэтому мы используем или для того, чтобы получить список накопителей extended: a или b return b, если bool (a) оценивается как false. 14 май. 142014-05-14 19:21:21

  0

@AlexMartelli Можете ли вы предложить, будет ли это все еще лучшим вариантом, если все подсписные буквы имеют одинаковую длину (в моем случае длина 2)? 04 янв. 152015-01-04 12:55:36

+38

@Joel, фактически в настоящее время 'list (itertools.chain.from_iterable (l))' лучше - как замечено в других комментариях и ответе Шона. 04 янв. 152015-01-04 15:40:56

  0

Моя попытка понять [item **, ** для подсписок в l-> для элемента в подсписке] 12 фев. 152015-02-12 11:57:15

  0

Действительно хорошая иллюстрация! Версии, основанные на 'sum' /' reduce', становятся жертвами алгоритма [Schlemiel the Painter's] (// en.wikipedia.org/wiki/Joel_Spolsky#Schlemiel_the_Painter.27s_algorithm), антипаттерн, именуемый как основателем Stack Overflow Joel Spolsky:) 11 июл. 152015-07-11 05:19:01

  0

Извините, что возродил эту очень старую нить, но мне было любопытно, почему это решение является лучшим. Если я правильно читаю вывод 'timeit', каждый цикл этого решения работает примерно в 10 раз быстрее, но в 10 раз больше циклов. 26 авг. 152015-08-26 13:06:14

+2

@JuanXarg, нет, вы полностью недопонимаете вывод 'timeit': он повторяет еще 10 раз, потому что он * может * (в пределах грубого ограничения примерно на такое же количество прошедшего времени), а не потому, что итерации находятся в любой форме, форме или форме, «требуется». 28 авг. 152015-08-28 21:50:27

  0

Любой 'sublist', который на самом деле не является списком, но' str' тоже будет разделен, так или иначе, чтобы уклониться от этого на питоническом пути? 10 июн. 162016-06-10 18:13:36

+2

Меня не волнует, как быстро он работает, пока его трудно понять! Может ли кто-нибудь сказать мне, что он может запомнить синтаксис через 3 месяца, не обращаясь снова к этому же сообщению? Может ли кто-нибудь сказать мне, почему вам нужно повторить 'item for sublist'? Ugly Ugly Ugly !! 28 сен. 162016-09-28 18:38:30

+3

Почему все upvotes? 'reduce (operator.concat, list2d)' быстрее и проще для понимания! См. [Этот ответ] (http://stackoverflow.com/a/39493960/2529619). 30 окт. 162016-10-30 02:29:56

  0

Я попытался поместить круглые скобки, чтобы прояснить ассоциативность этих (вложенных?) Генераторов .. но я до сих пор не сработал. Ни один из «list (i для a in ([[1, 2], [3, 4]] для i в a))' и 'list ((i для a в [[1, 2], [3, 4] ]]) для i в a) 'compile, но дают синтаксические ошибки. Я понимаю, что последний «а» относится к первому «а», представленному там. Поэтому я предположил, что выражение должно быть лево-ассоциативным. Но последний тоже не разбирается. Любой намек на это? 09 янв. 172017-01-09 18:02:29

  0

@ KennethJiang Я считаю, что это очень легко запомнить. Просто используйте: '[x для x в x для x в x]'. Это стало одним из моих любимых: D 10 мар. 172017-03-10 14:25:08

+3

@JohnMee: '[лист для листа в дереве для дерева в лесу]' было бы еще лучше и намного легче читать, но, к сожалению, разработчики Python так не думали. 15 апр. 172017-04-15 16:06:12

  0

@ Ожидаемая оценка эффективности в списке из 99 элементов вряд ли соответствует истинному эталону 30 июн. 172017-06-30 13:41:50

  0

Вначале синтаксис '[лист для дерева в лесу для листьев в дереве] кажется запутанным и неправильным, но его то же, что и вложенный for loop, за исключением того, что 'yield leaf' перемещается на передний план, чтобы он соответствовал стандарту понимания списка. например 'для дерева в лесу: для листа в дереве: урожай лист'. Другой предложенный порядок синтаксиса '[лист для листа в дереве для дерева в лесу]' кажется гораздо более похожим на трубопровод или состав, а не на вложение, так что да, с этой точки зрения легче рассуждать об этом глубоком гнезде. 12 сен. 172017-09-12 03:42:25

  0

Хотелось бы, чтобы в Python было что-то вроде пакета magrittr R для функций соединения. Обычно легче читать слева направо, а не изнутри, хотя, возможно, это очень предвзятая перспектива от кого-то, кто изначально пишет левый-правый язык, такой как английский. 12 сен. 172017-09-12 03:51:50


79
from functools import reduce #python 3 

>>> l = [[1,2,3],[4,5,6], [7], [8,9]] 
>>> reduce(lambda x,y: x+y,l) 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 

Метод extend() в вашем примере изменяет x вместо возвращения полезной стоимости (которая reduce() ожидает).

Более быстрый способ сделать версию reduce будет

>>> import operator 
>>> l = [[1,2,3],[4,5,6], [7], [8,9]] 
>>> reduce(operator.concat, l) 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 
+6

'reduce (operator.add, l)' будет быть правильным способом сделать «уменьшить» версию. Встроенные функции быстрее, чем lambdas. 24 сен. 112011-09-24 10:04:39

+1

@agf вот так: * 'timeit.timeit ('reduce (operator.add, l)', 'import operator; l = [[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] ', число = 10000) '** 0.017956018447875977 ** *' timeit.timeit (' reduce (lambda x, y: x + y, l) ',' import operator, l = [[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] ', число = 10000) '** 0.025218963623046875 ** 20 мар. 122012-03-20 22:13:11

+6

Это алгоритм художника Шлемиля joelonsoftware.com/articles/fog0000000319.html 25 апр. 122012-04-25 18:26:07

+2

, который может использоваться только для целых чисел. Но что, если в списке содержится строка? 11 сен. 152015-09-11 07:16:54

+2

@Freddy: Функция 'operator.add' работает одинаково хорошо для обоих списков целых чисел и списков строк. 11 сен. 152015-09-11 07:38:59

  0

Пожалуйста, отредактируйте это! Правильный оператор - 'concat', как показано здесь: https://stackoverflow.com/a/39493960/281545 28 май. 172017-05-28 13:49:01

+1

@Mr_and_Mrs_D: Исправлено, спасибо. 28 май. 172017-05-28 17:51:38

  0

from functools import уменьшить Добро пожаловать. 02 янв. 182018-01-02 02:42:55


586

Примечание от автора: Это неэффективно. Но весело, потому что монады потрясающие. Это не подходит для производственного кода Python.

>>> sum(l, []) 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 

Это просто суммирует элементы Iterable принят в первом аргументе, рассматривая второй аргумент в качестве начального значения суммы (если не задано, 0 используется вместо и этот случай даст вам сообщение об ошибке).

Поскольку вы суммируете вложенные списки, вы фактически получаете [1,3]+[2,4] в результате sum([[1,3],[2,4]],[]), что равно [1,3,2,4].

Обратите внимание, что работает только в списках списков. Для списков списков списков вам понадобится другое решение.

+1

См. Также ответ Надии. Это, по-видимому, самое быстрое решение. 04 июн. 092009-06-04 20:59:36

+70

это довольно аккуратно и умно, но я бы не использовал его, потому что это путано читать. 15 июн. 102010-06-15 18:55:14

  0

@Nick: Не сразу понятно, что вы можете это сделать. Несмотря на то, что + перегружен, функция sum реализована в C. Я полагаю, что она была недавно изменена для поддержки не числовых объектов. 20 май. 112011-05-20 17:31:07

+67

Это алгоритм художника Шлемиля http://www.joelonsoftware.com/articles/fog0000000319.html - излишне неэффективный, а также излишне уродливый. 25 апр. 122012-04-25 18:24:57

+1

@ThomasAhle, я думаю, вы можете быть смущены. 25 апр. 122012-04-25 18:25:34

+22

Операция добавления в списках формирует ** ['Monoid'] (http://en.wikipedia.org/wiki/Monoid#Monoids_in_computer_science) **, которая является одной из самых удобных абстракций для мышления' + ' в общем смысле (не ограничиваясь только номерами). Поэтому этот ответ заслуживает +1 от меня за (правильное) обращение с списками как моноид. * Производительность касается, хотя ... * 03 дек. 142014-12-03 10:35:23

+2

@andrewrk Ну, некоторые люди считают, что это самый чистый способ сделать это: https://www.youtube.com/watch?v=IOiZatlZtGU те, кто не понимает почему это круто, просто нужно подождать несколько десятилетий, пока все не сделают это так: давайте будем использовать языки программирования (и абстракции), которые будут обнаружены и не изобретены, обнаружен Monoid. 05 окт. 152015-10-05 08:51:42

+2

Это очень неэффективный способ из-за квадратичного аспекта суммы. 31 июл. 172017-07-31 18:04:59

+1

В этой статье объясняются математические данные о неэффективности https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/ 04 янв. 182018-01-04 16:46:19

  0

Это, похоже, не работает в python 2.7, если только Я делаю что-то не так. Когда я имею дело со строками: 'TypeError: может только конкатенировать список (а не« str ») для списка. Когда я меняю его на ints, я получаю ту же ошибку, но' s/str/int/'. Я предполагал, что «сумма» является стандартным оператором ...? 08 фев. 182018-02-08 20:04:51

  0

Значит, это нормально для коротких случаев? 20 фев. 182018-02-20 01:28:46


23

Почему вы используете удлинитель?

reduce(lambda x, y: x+y, l) 

Это должно работать нормально.

+11

Это, вероятно, создает много-много промежуточных списков. 23 май. 162016-05-23 19:33:14

+2

для python3 '' 'from functools import reduce''' 19 янв. 172017-01-19 18:15:10

  0

Извините, что очень медленно см. Остальную часть ответов 29 май. 172017-05-29 12:04:16


29

Я возвращаю свое заявление. сумма не является победителем. Хотя это быстрее, когда список невелик. Но производительность значительно ухудшается при использовании больших списков.

>>> timeit.Timer(
     '[item for sublist in l for item in sublist]', 
     'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000' 
    ).timeit(100) 
2.0440959930419922 

версия сумма по-прежнему работает более чем за минуту, и это еще не сделано обработки еще!

Для средних списков:

>>> timeit.Timer(
     '[item for sublist in l for item in sublist]', 
     'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10' 
    ).timeit() 
20.126545906066895 
>>> timeit.Timer(
     'reduce(lambda x,y: x+y,l)', 
     'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10' 
    ).timeit() 
22.242258071899414 
>>> timeit.Timer(
     'sum(l, [])', 
     'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10' 
    ).timeit() 
16.449732065200806 

Используя небольшие списки и timeit: число = 1000000

>>> timeit.Timer(
     '[item for sublist in l for item in sublist]', 
     'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]' 
    ).timeit() 
2.4598159790039062 
>>> timeit.Timer(
     'reduce(lambda x,y: x+y,l)', 
     'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]' 
    ).timeit() 
1.5289170742034912 
>>> timeit.Timer(
     'sum(l, [])', 
     'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]' 
    ).timeit() 
1.0598428249359131 
+19

для действительно миниатюрного списка, например. один из них с 3 подсписками, возможно, но, поскольку производительность суммы идет с O (N ** 2), в то время как понимание списка идет с O (N), только увеличение списка ввода немного изменит ситуацию - действительно, LC будет " бесконечно быстрее ", чем сумма на пределе при увеличении N.Я отвечал за проектирование суммы и выполнение ее первой реализации в среде исполнения Python, и мне все же хотелось бы, чтобы я нашел способ эффективно ограничить ее суммированием чисел (на что он действительно хорош) и блокировать «привлекательную неприятность», которую он предлагает людям которые хотят «суммировать» списки ;-). 04 июн. 092009-06-04 21:07:24


10

Причина ваша функция не работает: удлинять расширяет массив на месте и Безразлично» t верните его. Вы все еще можете вернуть x из лямбда, используя какой-то трюк:

reduce(lambda x,y: x.extend(y) or x, l) 

Примечание: удлинение более эффективно, чем + в списках.

+5

'extend' лучше использовать как' newlist = [] ',' extend = newlist.extend', 'для подписок в l: extend (l)', поскольку он избегает (довольно больших) накладных расходов 'lambda', поиск атрибутов на 'x' и' or'. 24 сен. 112011-09-24 10:12:35


979

Вы можете использовать itertools.chain():

>>> import itertools 
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]] 
>>> merged = list(itertools.chain(*list2d)) 

или на Python> = 2.6, используйте itertools.chain.from_iterable(), который не требует распаковки списка:

>>> import itertools 
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]] 
>>> merged = list(itertools.chain.from_iterable(list2d)) 

Этот подход является, возможно, более удобным для чтения, чем [item for sublist in l for item in sublist] и оказывается более быстрым:

[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))' 
10000 loops, best of 3: 24.2 usec per loop 
[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]' 
10000 loops, best of 3: 45.2 usec per loop 
[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])' 
1000 loops, best of 3: 488 usec per loop 
[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)' 
1000 loops, best of 3: 522 usec per loop 
[[email protected]]$ python --version 
Python 2.7.3 
+9

@ShawnChin BTW, часть оборудования, которое вы имели, отвечая на этот вопрос, моя текущая рабочая станция наполовину так же быстро и прошло 4 года. 24 сен. 132013-09-24 15:18:59

  0

@alexandre см. Https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument-lists 23 июл. 142014-07-23 20:15:23

+3

«*» - это сложная вещь, которая делает «цепочку» менее простой, чем понимание списка. Вы должны знать, что цепочка соединяется вместе только итерами, передаваемыми как параметры, и * заставляет список верхнего уровня расширяться в параметры, поэтому «цепочка» объединяет все эти итерации, но не спускается дальше. Я думаю, что это делает понимание более читаемым, чем использование цепочки в этом случае. 03 сен. 142014-09-03 14:13:45

+12

@TimDierks: Я не уверен, что «это требует от вас понимания синтаксиса Python» - это аргумент против использования данной методики в Python. Разумеется, сложное использование может смутить, но оператор «splat» обычно полезен во многих случаях, и это не использует его особенно неясным образом; отвергая все языковые функции, которые не обязательно очевидны для начинающих пользователей, означает, что вы связываете одну руку за спиной. Может также выкинуть список понятий, пока вы на нем; пользователи из других фонов найдут цикл 'for', который повторно добавит' s более очевидным. 12 ноя. 152015-11-12 20:26:36

  0

Что насчет '['abcde_', ['_abcde', ['e_abcd', ['de_abc', ['cde_ab', ['bcde_a']]]]]]' 07 дек. 152015-12-07 19:24:50

  0

Это медленнее, чем предложил Алекс в контекст ниже '_all_altered_nbrs = {" nbr1 ": {" key1 ":" src2 "}," nbr2 ": {" key2 ":" src4 "}}% timeit [k для ключа в _all_altered_nbrs.itervalues ​​() для k в key.keys()] % timeit reduce (lambda x, y: x.extend (y.keys()) или x, _all_altered_nbrs.itervalues ​​(), []) % список времени (itertools.chain (* [src_relation .keys() для src_relation в _all_altered_nbrs.itervalues ​​()])) '' 100000 петли, лучше от 3: 3,47 мкс на петле \t \t 100000 петли, лучшие из 3: 6,74 мкс на петле \t \t 100000 петли, лучшие из 3: 12,7 мкс за цикл' 21 апр. 162016-04-21 06:48:03

  0

Python 3.6+ версия: '[* chain.from_iterable (продукт (* list2d)))] ', предполагая, что вам действительно нужен этот список, и не может просто перебирать непосредственно элементы. 12 мар. 172017-03-12 10:36:40


4

Можно также использовать NumPy-х flat:

import numpy as np 
list(np.array(l).flat) 

Редактировать 11/02/2016: Работает только когда подсписки имеют одинаковые размеры.

  0

было бы оптимальным решением? 22 сен. 162016-09-22 20:08:18


18

Существует, кажется, путаница с operator.add! Когда вы добавляете два списка вместе, правильный термин для этого - concat, а не добавить. operator.concat - это то, что вам нужно использовать.

Если вы думаете, функциональный, это так просто, как это ::

>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9)) 
>>> reduce(operator.concat, list2d) 
(1, 2, 3, 4, 5, 6, 7, 8, 9) 

Вы видите уменьшить Уважает тип последовательности, поэтому, когда вы поставляете кортеж, вы получите обратно кортеж. давайте попробуем со списком:

>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]] 
>>> reduce(operator.concat, list2d) 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 

Ага, вы возвращаете список.

Как насчет производительности ::

>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]] 
>>> %timeit list(itertools.chain.from_iterable(list2d)) 
1000000 loops, best of 3: 1.36 µs per loop 

from_iterable довольно быстро! Но это не сравнение с concat.

>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9)) 
>>> %timeit reduce(operator.concat, list2d) 
1000000 loops, best of 3: 492 ns per loop 
  0

Хм быть справедливым вторым примером также должен быть список (или первый кортеж?) 28 май. 172017-05-28 13:20:00


4
def flatten(l, a): 
    for i in l: 
     if isinstance(i, list): 
      flatten(i, a) 
     else: 
      a.append(i) 
    return a 

print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], [])) 

# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6] 
  0

Не работает для меня, если я вручную не укажу 'a = []': '>>> flatten ([[1,2,3], [4,5,6]]) [1, 2, 3, 4, 5, 6] >>> flatten ([[1,2,3], [4,5,6]]) [1 , 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6] ' 06 ноя. 162016-11-06 22:52:10

  0

@Jeff Мой ответ был отредактирован @ deleet ... Проверить мой оригинальный ответ и он работает ... 08 ноя. 162016-11-08 03:04:53

+1

nice, Благодарю. Я откатил @ deleet's edit 08 ноя. 162016-11-08 18:21:18

+2

Да, это было плохо, как я узнал позже! Я проверил его, но ошибка произошла только после первого запуска. Причина в том, что аргумент по умолчанию [] рассматривается как согласованный объект в Python. Поэтому в следующий раз, когда вы запустите функцию, она начинается с списка, который вы использовали в прошлый раз! Очень неприятная ошибка, трудно понять. В R (который я использую в основном) это сработало бы из-за семантики копирования. Кто-нибудь знает решение Python? При необходимости вручную указывать пустой список каждый раз не является хорошим дизайном. Мне нужна эта функция для моего собственного проекта, поэтому я надеюсь, что кто-то знает. :) 09 ноя. 162016-11-09 03:27:01

  0

Я отправил исправленную версию сейчас. 11 ноя. 162016-11-11 11:53:48

  0

'def flatten (l, a = None): если a None: a = []' [...] 22 фев. 182018-02-22 23:28:38


2

Если вы готовы отказаться от небольшого количества скорости для более чистого взгляда, то вы можете использовать numpy.concatenate().tolist() или numpy.concatenate().ravel().tolist():

import numpy 

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99 

%timeit numpy.concatenate(l).ravel().tolist() 
1000 loops, best of 3: 313 µs per loop 

%timeit numpy.concatenate(l).tolist() 
1000 loops, best of 3: 312 µs per loop 

%timeit [item for sublist in l for item in sublist] 
1000 loops, best of 3: 31.5 µs per loop 

Вы можете узнать больше здесь в docs numpy.concatenate и numpy.ravel


7

Плохая особенность функции Anil выше заключается в том, что пользователю требуется всегда вручную указывать второй аргумент nt - пустой список []. Вместо этого это будет дефолт. Из-за того, как работают объекты Python, они должны быть установлены внутри функции, а не в аргументах.

Вот рабочая функция:

def list_flatten(l, a=None): 
    #check a 
    if a is None: 
     #initialize with empty list 
     a = [] 

    for i in l: 
     if isinstance(i, list): 
      list_flatten(i, a) 
     else: 
      a.append(i) 
    return a 

Тестирование:

In [2]: lst = [1, 2, [3], [[4]],[5,[6]]] 

In [3]: lst 
Out[3]: [1, 2, [3], [[4]], [5, [6]]] 

In [11]: list_flatten(lst) 
Out[11]: [1, 2, 3, 4, 5, 6] 

10

Если вы хотите, чтобы сгладить структуры данных, где вы не знаете, как глубоко он вложен вы можете использовать iteration_utilities.deepflatten

>>> from iteration_utilities import deepflatten 

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] 
>>> list(deepflatten(l, depth=1)) 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 

>>> l = [[1, 2, 3], [4, [5, 6]], 7, [8, 9]] 
>>> list(deepflatten(l)) 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 

Это генератор поэтому вам нужно передать результат в list или явно перебрать его.


Чтобы распрямить только один уровень, и если каждый из элементов сама итерацию вы также можете использовать iteration_utilities.flatten, который сам по себе является лишь тонкая оболочка вокруг itertools.chain.from_iterable:

>>> from iteration_utilities import flatten 
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] 
>>> list(flatten(l)) 
[1, 2, 3, 4, 5, 6, 7, 8, 9] 

1 Отказ от ответственности: Я являюсь автором этой библиотеки


2

Самое быстрое решение, которое я нашел (для большого списка в любом случае):

import numpy as np 
#turn list into an array and flatten() 
np.array(l).flatten() 

Done! Вы можете, конечно, включить его в список, выполнив список (l)

  0

Это неправильно, сглаживание уменьшит размеры n-го массива до одного, но не объединит списки внутри как один. 30 июн. 172017-06-30 08:15:10


29

Вот общий подход, который применяется к вложенным спискам списков, чисел, строк и других типов смешанных контейнеров.

from collections import Iterable 


def flatten(items): 
    """Yield items from any nested iterable; see REF.""" 
    for x in items: 
     if isinstance(x, Iterable) and not isinstance(x, (str, bytes)): 
      yield from flatten(x) 
     else: 
      yield x 

list(flatten(l))        # list of lists 
#[1, 2, 3, 4, 5, 6, 7, 8, 9] 

items = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"] # numbers & mixed containers 
list(flatten(items)) 
#[1, 2, 3, 4, 5, 6, 7, 8, '9'] 

Это решение использует Python 3 мощный yield from ключевое слово, которое извлекает элементы из суб-генераторов. Примечание. Это решение не относится к строкам. UPDATE: теперь поддерживает строки.

REF: решение изменено с Beazley, D. and B. Jones. Рецепт 4.14, Python Cookbook 3rd Ed., O'Reilly Media Inc. Sebastopol, CA: 2013.

+1

Я просто написал почти то же самое, потому что я не видел вашего решения ... вот что я искал «рекурсивно сплющивать полные множественные списки» ... (+1) 25 мар. 172017-03-25 15:32:05

+2

@MartinThoma Очень ценим. FYI, если выравнивание вложенных итераций является обычной практикой для вас, есть некоторые сторонние пакеты, которые хорошо справляются с этим. Это может спастись от повторного использования колеса. Я упомянул «more_itertools» среди других, обсуждавшихся в этом посте. Приветствия. 25 мар. 172017-03-25 17:51:51

+1

Nice - просто задавался вопросом о построении типа «yield from» на python после изучения «yield *» в es2015. 14 апр. 172017-04-14 21:30:22

+1

заменить на 'if isinstance (el, collections.Iterable), а не isinstance (el, (str, bytes)):' для поддержки строк. 13 май. 172017-05-13 23:40:43

+1

Исправить. Исходный рецепт поваренной книги фактически показывает, как поддерживать строки и байты. Если они отредактировали его, чтобы отразить эту поддержку. 14 май. 172017-05-14 00:25:13

  0

Возможно, «traverse» также может быть хорошим именем для этого пути дерева, тогда как я бы сохранил его менее * универсальным * для этого ответа, придерживаясь вложенных списков. 15 июн. 172017-06-15 10:22:27


7

Рассмотрите возможность установки пакета more_itertools.

> pip install more_itertools 

Он поставляется с реализацией для flatten (source, от itertools recipes):

import more_itertools 

# Using flatten() 
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] 
list(more_itertools.flatten(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9] 

В версии 2.4, вы можете сплющивающиеся более сложные, вложенные итерируемых с more_itertools.collapse (source, вносимой abarnet).

# Using collapse() 
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]  # given example 
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9] 

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9] # complex nesting 
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9] 

1

Подчищены пример @Deleet

from collections import Iterable 

def flatten(l, a=[]): 
    for i in l: 
     if isinstance(i, Iterable): 
      flatten(i, a) 
     else: 
      a.append(i) 
    return a 

daList = [[1,4],[5,6],[23,22,234,2],[2], [ [[1,2],[1,2]],[[11,2],[11,22]] ] ] 

print(flatten(daList)) 

Пример: https://repl.it/G8mb/0

+3

Спасибо за подход. У меня есть один комментарий к вашему ключевому слову 'a'. [Mutable default args] (http://docs.python-guide.org/en/latest/writing/gotchas/) имеют побочные эффекты и их часто избегают в Python. У этой ссылки есть решение. Приветствия. 25 мар. 172017-03-25 18:22:52


4

Простой код для underscore.py пакета вентилятора

from underscore import _ 
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]]) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9] 

Это решает все проблемы сплющить (нет элемента списка или комплекса гнездование)

from underscore import _ 
# 1 is none list item 
# [2, [3]] is complex nesting 
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]]) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9] 

Вы можете установить underscore.py с пипом

pip install underscore.py 
  0

Аналогичным образом вы можете использовать [pydash] (https://github.com/dgilland/pydash). Я считаю эту версию более читаемой, чем понимание списка или любые другие ответы. 06 июн. 172017-06-06 03:22:40

  0

Это очень медленно. 26 июл. 172017-07-26 09:52:16


6

После показаться простейшими мне:

>>> import numpy as np 
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] 
>>> print (np.concatenate(l)) 
[1 2 3 4 5 6 7 8 9] 

55

Я проверил большинство предлагаемых решений с perfplot (любимым проектом шахты, по существу обертка timeit), и найдено

list(itertools.chain.from_iterable(a))

будет самым быстрым решением (если конкатенировано более 10 списков).

enter image description here


Код для воспроизведения сюжета:

import functools 
import itertools 
import numpy 
import operator 
import perfplot 


def forfor(a): 
    return [item for sublist in a for item in sublist] 


def sum_brackets(a): 
    return sum(a, []) 


def functools_reduce(a): 
    return functools.reduce(operator.concat, a) 


def itertools_chain(a): 
    return list(itertools.chain.from_iterable(a)) 


def numpy_flat(a): 
    return list(numpy.array(a).flat) 


def numpy_concatenate(a): 
    return list(numpy.concatenate(a)) 


perfplot.show(
    setup=lambda n: [range(10)] * n, 
    kernels=[ 
     forfor, sum_brackets, functools_reduce, itertools_chain, numpy_flat, 
     numpy_concatenate 
     ], 
    n_range=[2**k for k in range(12)], 
    logx=True, 
    logy=True, 
    xlabel='num lists' 
    ) 

3
def flatten(alist): 
    if alist == []: 
     return [] 
    elif type(alist) is not list: 
     return [alist] 
    else: 
     return flatten(alist[0]) + flatten(alist[1:]) 

2

Недавно я наткнулся на ситуацию, где у меня был смесь строк и числовых данных в подсписках, такие как

test = ['591212948', 
['special', 'assoc', 'of', 'Chicago', 'Jon', 'Doe'], 
['Jon'], 
['Doe'], 
['fl'], 
92001, 
555555555, 
'hello', 
['hello2', 'a'], 
'b', 
['hello33', ['z', 'w'], 'b']] 

, где методы, подобные flat_list = [item for sublist in test for item in sublist], не сработали. Итак, я придумал следующее решение для 1+ уровня подсписков

def concatList(data): 
    results = [] 
    for rec in data: 
     if type(rec) == list: 
      results += rec 
      results = concatList(results) 
     else: 
      results.append(rec) 
    return results 

И результат

In [38]: concatList(test) 
Out[38]: 
Out[60]: 
['591212948', 
'special', 
'assoc', 
'of', 
'Chicago', 
'Jon', 
'Doe', 
'Jon', 
'Doe', 
'fl', 
92001, 
555555555, 
'hello', 
'hello2', 
'a', 
'b', 
'hello33', 
'z', 
'w', 
'b'] 

0

Вы можете избежать рекурсивных вызовов в стеке, используя фактическую структуру стека данных довольно просто.

alist = [1,[1,2],[1,2,[4,5,6],3, "33"]] 
newlist = [] 

while len(alist) > 0 : 
    templist = alist.pop() 
    if type(templist) == type(list()) : 
    while len(templist) > 0 : 
     temp = templist.pop() 
     if type(temp) == type(list()) : 
     for x in temp : 
      templist.append(x) 
     else : 
     newlist.append(temp) 
    else : 
    newlist.append(templist) 
print(list(reversed(newlist))) 

1

Интересно отметить, в дополнение,
такая задача не может быть выполнена с помощью 'распаковки'

In [46]: [*i for i in l] 
SyntaxError: iterable unpacking cannot be used in comprehension 

вложенным понимание следует применять

In [47]: [i for j in l for i in j] 
Out[47]: [1, 2, 3, 4, 5, 6, 7, 8, 9] 

0

Не это наиболее очевидное решение
fList = eval('[' + str(l).replace('[','').replace(']','') + ']') # Just for fun


1

По моему требованию уплощения всего списка строк в один список, следующий подход работает:

list(itertools.chain(*listoflists)) 

с числовыми данными, другие подходы работают хорошо. В случае строк вы можете попробовать это.


0

Вы можете попробовать это:

weird_list=[[1, 2, 3], [4, 5, 6], [7], [8, 9]] 
nice_list = [int(e) for e in str(a) if e not in "[] ,"] 

2

Еще один необычный подход, который работает для гетеро- и однородных списков целых чисел:

def unusual_flatten(some_list: list) -> list: 
    cleaned_list = str(some_list).replace(("["), "").replace("]", "").split(",") 
    return [int(item) for item in cleaned_list] 

претендующих на примере список ...

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10] 

unusual_flatten(l) 

Результат:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

  0

Это просто более сложный и немного более медленный способ того, что уже было опубликовано ранее. Я заново изобрел его предложение вчера, поэтому этот подход кажется довольно популярным в наши дни;) 10 янв. 182018-01-10 22:03:32

  0

Не совсем: 'wierd_list = [[1, 2, 3], [4, 5, 6], [7], [8, 9] , 10] ' >>' nice_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0] ' 11 янв. 182018-01-11 08:17:10

  0

Мой код как один вкладыш будет: ' flat_list = [int (e.replace ('[', ''). replace (']', '')) для e в str (deep_list) .split (',')] ' 11 янв. 182018-01-11 08:32:18

  0

Вы действительно правы +1, предложение №3000 выиграло Я работаю с несколькими номерами цифр, я также не тестировал это раньше, хотя это должно быть очевидно. Вы можете упростить свой код и написать '[int (e.strip ('[]')) для e в str (deep_list) .split (',')]'. Но я предлагаю придерживаться предложения Деле для реальных случаев использования. Он не содержит преобразований хакерского типа, он быстрее и универсален, потому что он, естественно, также обрабатывает списки со смешанными типами. 11 янв. 182018-01-11 16:31:09

  0

Спасибо! Это, конечно, было смешно. Раньше я видел предложение Deleet в книге python. 13 янв. 182018-01-13 08:02:22

  0

Можете ли вы рассказать нам, какую книгу? Я много размышлял об этом, потому что он настолько эффективен и красив. Повредит предел рекурсии неизбежно вообще, но для подобных случаев с небольшим количеством рекурсий он кажется идеальным. 13 янв. 182018-01-13 16:04:15

+1

К сожалению нет. Но я недавно увидел этот код здесь: [Практическая книга Питона] (http://anandology.com/python-practice-book/functional-programming.html) 6.1.2 15 янв. 182018-01-15 08:18:01


0

Это может быть сделано с помощью toolz.concat или cytoolz.concat (cythonized версия, что может быть быстрее в некоторых случаях):

from cytoolz import concat 
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] 
list(concat(l)) # or just `concat(l)` if one only wants to iterate over the items 

На моем компьютере, в питон 3.6, это, кажется, время почти так же быстро, а [item for sublist in l for item in sublist] (не считая время импорта):

In [611]: %timeit L = [item for sublist in l for item in sublist] 
695 ns ± 2.75 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 

In [612]: %timeit L = [item for sublist in l for item in sublist] 
701 ns ± 5.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 

In [613]: %timeit L = list(concat(l)) 
719 ns ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 

In [614]: %timeit L = list(concat(l)) 
719 ns ± 22.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 

версия toolz является действительно медленнее:

In [618]: from toolz import concat 

In [619]: %timeit L = list(concat(l)) 
845 ns ± 29 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 

In [620]: %timeit L = list(concat(l)) 
833 ns ± 8.73 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 

3

matplotlib.cbook.flatten() будет работать для вложенных списков, даже если они гнездятся более глубоко, чем пример.

import matplotlib 
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] 
print(list(matplotlib.cbook.flatten(l))) 
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]] 
print list(matplotlib.cbook.flatten(l2)) 

Результат:

[1, 2, 3, 4, 5, 6, 7, 8, 9] 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 

0

Примечание: Ниже относится к Python 3.3+, поскольку он использует yield_from.


В случае obj = [[1, 2,], [3, 4], [5, 6]], все решения здесь хороши, в том числе список понимания и itertools.chain.from_iterable.

Однако рассмотрим это немного более сложный случай:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]] 

Есть несколько проблем:

  • Одним из элементов, 6, просто скаляр; это не повторим, поэтому указанные выше маршруты не сработают.
  • Один элемент, 'abc', is технически итерируемый (все str s есть). Однако, читая между строк немного, мы не хотим рассматривать его как таковое - мы хотим рассматривать его как один элемент.
  • Последний элемент, [8, [9, 10]] сам по себе является вложенным итерабельным. Основное понимание списка и chain.from_iterable извлекают только «1 уровень вниз».

Мы можем исправить это следующим образом:

>>> import sys 
>>> from collections import Iterator 

>>> py2 = sys.version_info[0] == 2 

>>> if py2: 
...  str_types = basestring, unicode, str 
... else: 
... str_types = str, bytes 


>>> def flatten(obj): 
...  for i in obj: 
...   if isinstance(i, Iterable) and not isinstance(i, str_types): 
...    yield from flatten(i) 
...   else: 
...    yield i 


>>> list(flatten(obj)) 
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10] 

Здесь мы проверяем, что суб-элемент (1) является итерацию с Iterable, БАТ из itertools, но и хотят, чтобы убедиться, что (2) элемент равен не "string-like." Первые несколько строк обеспечивают совместимость между Python 2 & 3.

Это конкурирует с или волосы быстрее, чем matplotlib.cbook.flatten:

>>> from matplotlib.cbook import flatten as m_flatten 

%timeit flatten(obj*100) 
1.39 µs ± 21.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 

%timeit m_flatten(obj*100) 
1.43 µs ± 17.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 
  0

Я не думаю, что «уступят из flatten (i) 'будет работать для python <3.3, не так ли? 01 фев. 182018-02-01 20:31:08