Как сделать плоский список из списка списков?

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

Я могу сделать это в forцикле, но, может быть, есть какой-то крутой "однострочник"? Я попробовал это с reduce(), но я получаю ошибку.

Код

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'
4.06.2009 20:30:05
Здесь подробно обсуждается это: rightfootin.blogspot.com/2006/09/more-on-python-flatten.html , где обсуждаются несколько методов сглаживания произвольно вложенных списков списков. Интересное чтение!
RichieHindle 4.06.2009 20:41:13
Некоторые другие ответы лучше, но причина, по которой вы терпите неудачу, состоит в том, что метод extension всегда возвращает None. Для списка длиной 2 он будет работать, но вернет None. Для более длинного списка он будет использовать первые 2 аргумента, который возвращает None. Затем он продолжается с None.extend (<третий аргумент>), что вызывает эту
mehtunguh 11.06.2013 21:48:27
Решение @ shawn-chin здесь более питонное, но если вам нужно сохранить тип последовательности, скажем, у вас есть кортеж кортежей, а не список списков, тогда вам следует использовать redu (operator.concat, tuple_of_tuples). Использование operator.concat с кортежами работает быстрее, чем chain.from_iterables со списком.
Meitham 6.10.2014 21:46:22
30 ОТВЕТОВ
РЕШЕНИЕ

Учитывая список списков l,

flat_list = [item for sublist in l for item in sublist]

что значит:

flat_list = []
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 подсписков - поскольку список промежуточных результатов продолжает увеличиваться, на каждом шаге выделяется новый объект списка промежуточных результатов, и все элементы в предыдущем промежуточном результате его необходимо скопировать (а также добавить несколько новых в конце). Итак, для простоты и без фактической потери общности, скажем, у вас есть L подсписков из I элементов каждый: первые I элементы копируются назад и вперед L-1 раз, вторые I элементы L-2 раза и т. Д .; Общее количество копий I раз суммы х для й от 1 до L исключенного, т I * (L**2)/2.

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

4664
29.03.2019 10:29:16
Я попробовал тест с теми же данными, используя itertools.chain.from_iterable: $ python -mtimeit -s'from itertools import chain; l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'list(chain.from_iterable(l))'. Он работает чуть более чем в два раза быстрее, чем понимание вложенного списка, что является самой быстрой из представленных здесь альтернатив.
intuited 15.10.2010 01:21:33
Мне было трудно понять синтаксис, пока я не понял, что вы можете думать о нем точно так же, как о вложенных циклах. для подсписка в l: для элемента в подсписке: yield item
Rob Crowell 27.07.2011 16:43:18
@BorisChervenkov: Обратите внимание, что я завернул вызов, list()чтобы реализовать итератор в списке.
intuited 20.05.2012 22:56:40
[лист для дерева в лесу для листа в дереве] может быть легче понять и применить.
John Mee 29.08.2013 01:38:20
@ Джоэл, на самом деле в наши дни list(itertools.chain.from_iterable(l))это лучше всего - как отмечалось в других комментариях и ответе Шона.
Alex Martelli 4.01.2015 15:40:56
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]
177
2.01.2018 05:02:41
reduce(operator.add, l)будет правильным способом сделать reduceверсию. Встроенные модули работают быстрее, чем лямбды.
agf 24.09.2011 10:04:39
@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]]', number=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]]', number=10000) 0.025218963623046875
lukmdo 20.03.2012 22:13:11
Это алгоритм художника Шлемеля joelonsoftware.com/articles/fog0000000319.html
Mike Graham 25.04.2012 18:26:07
это можно использовать только для integers. Но что, если список содержит string?
Freddy 11.09.2015 07:16:54
@Freddy: operator.addфункция работает одинаково хорошо как для списков целых чисел, так и для списков строк.
Greg Hewgill 11.09.2015 07:38:59

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

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

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

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

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

886
29.12.2018 16:00:39
это довольно опрятно и умно, но я бы не стал его использовать, потому что читать смущает.
andrewrk 15.06.2010 18:55:14
Это алгоритм художника Шлемеля joelonsoftware.com/articles/fog0000000319.html - излишне неэффективный, а также излишне уродливый.
Mike Graham 25.04.2012 18:24:57
Операция добавления в списки формирует a Monoid, которая является одной из наиболее удобных абстракций для представления +операции в общем смысле (не ограничиваясь только числами). Так что этот ответ заслуживает от меня +1 за (правильную) обработку списков как моноидов. Спектакль касается, хотя ...
ulidtko 3.12.2014 10:35:23
@andrewrk Ну, некоторые люди думают, что это самый чистый способ сделать это: youtube.com/watch?v=IOiZatlZtGU тем, кто не понимает, почему это круто, просто нужно подождать несколько десятилетий, пока все не сделают это таким образом: ) давайте использовать языки программирования (и абстракции), которые открыты и не изобретены, Monoid обнаружен.
jhegedus 5.10.2015 08:51:42
это очень неэффективный способ из-за квадратичного аспекта суммы.
Jean-François Fabre♦ 31.07.2017 18:04:59

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

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

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

33
31.12.2014 12:07:09
для python3from functools import reduce
andorov 19.01.2017 18:15:10
Извините, что очень медленно, посмотрите остальные ответы
Mr_and_Mrs_D 29.05.2017 12:04:16
Это, безусловно, самое простое для понимания, но короткое решение, которое работает на Python 2 и 3. Я понимаю, что многие пользователи Python занимаются обработкой данных, где требуется обрабатывать огромные объемы данных и, таким образом, много заботиться о скорости, но когда вы пишут сценарий оболочки и имеют только несколько десятков элементов в нескольких подсписках, тогда это идеально.
Asfand Qazi 7.09.2018 13:36:56

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

>>> 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: number = 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
39
23.12.2013 09:14:58
для действительно крошечного списка, например, одного с 3-мя подсписками, может быть - но поскольку производительность sum идет с O (N ** 2), в то время как понимание списка идет с O (N), простое увеличение входного списка немного перевернет вещи - - действительно, LC будет «бесконечно быстрее», чем сумма на пределе, когда N растет. Я отвечал за проектирование sum и выполнение его первой реализации во время выполнения Python, и мне все еще хотелось бы найти способ эффективно ограничить его суммированием чисел (что действительно хорошо получается) и блокировать «привлекательную неприятность», которую он предлагает людям. кто хочет "суммировать" списки ;-).
Alex Martelli 4.06.2009 21:07:24

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

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

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

22
19.11.2019 16:26:36
extendлучше использовать в качестве newlist = [], extend = newlist.extend, for sublist in l: extend(l)поскольку он избегает (довольно большой) накладных расходов из lambda, что поиск атрибут x, и or.
agf 24.09.2011 10:12:35
для питона 3 добавитьfrom functools import reduce
Markus Dutschke 2.07.2019 12:24:14

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

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

Или вы можете использовать, itertools.chain.from_iterable()который не требует распаковки списка с *оператором :

import itertools
list2d = [[1,2,3], [4,5,6], [7], [8,9]]
merged = list(itertools.chain.from_iterable(list2d))
1531
24.03.2020 18:56:59
Это *сложная вещь, которая делает chainменее простой, чем понимание списка. Вы должны знать, что цепочка объединяет только итерируемые элементы, передаваемые в качестве параметров, и * заставляет список верхнего уровня расширяться до параметров, поэтому chainобъединяет все эти итерируемые элементы, но не опускается дальше. Я думаю, что это делает понимание более читабельным, чем использование цепочки в этом случае.
Tim Dierks 3.09.2014 14:13:45
@TimDierks: я не уверен, что «это требует от вас понимания синтаксиса Python» - это аргумент против использования данной техники в Python. Конечно, сложное использование может сбить с толку, но оператор «splat», как правило, полезен во многих обстоятельствах, и это не использует его особенно неясным образом; Отказ от всех языковых функций, которые не обязательно очевидны для начинающих пользователей, означает, что вы связываете одну руку за спиной. Также можно выкинуть списки из списка, пока вы на них; пользователи из других областей могли бы найти forпетлю, которая неоднократно становилась appendболее очевидной.
ShadowRanger 12.11.2015 20:26:36
Этот ответ и другие ответы здесь дают неверный результат, если верхний уровень также содержит значение. например, list = [["abc","bcd"],["cde","def"],"efg"]приведет к выводу["abc", "bcd", "cde", "def", "e", "f", "g"].
gouravkr 1.01.2020 10:39:50

Можно также использовать квартиру NumPy :

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

Редактировать 02.11.2016: Работает только тогда, когда подсписки имеют одинаковые размеры.

9
2.11.2016 09:59:40
это было бы оптимальным решением?
RetroCode 22.09.2016 20:08:18

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

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

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

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

>>> 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
38
9.05.2019 04:33:26
Хм, чтобы быть справедливым, второй пример также должен быть в списке (или первый кортеж?)
Mr_and_Mrs_D 28.05.2017 13:20:00
Использование таких небольших входных данных не очень хорошее сравнение. Для 1000 последовательностей длиной 1000 я получаю 0,037 секунды для list(chain.from_iterable(...))и 2,5 секунды для reduce(concat, ...). Проблема в том, что reduce(concat, ...)имеет квадратичное время выполнения, тогда как chainявляется линейным.
kaya3 18.12.2019 20:38:31
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]
17
28.11.2016 08:48:33
def flatten(l, a=None): if a is None: a = [][...]
Poik 22.02.2018 23:28:38

Если вы готовы отказаться от небольшого количества скорости для более чистого вида, то вы можете использовать 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

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

5
27.10.2016 03:31:54
Не работает для неравномерно вложенных списков, таких как[1, 2, [3], [[4]], [5, [6]]]
EL_DON 22.04.2019 21:39:37

Плохая особенность функции Anil, описанной выше, состоит в том, что она требует, чтобы пользователь всегда вручную указывал второй аргумент как пустой список []. Вместо этого это должно быть по умолчанию. Из-за того, как работают объекты 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]
13
29.11.2016 03:45:43

Если вы хотите сгладить структуру данных, где вы не знаете, как глубоко она вложена, вы можете использовать 1iteration_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]

Просто добавим немного времени (на основе ответа Нико Шлёмера, который не включает функцию, представленную в этом ответе):

введите описание изображения здесь

Это логарифмический сюжет для огромного диапазона значений. Для качественного рассуждения: чем ниже, тем лучше.

Результаты показывают , что если итератор содержит лишь несколько внутренних итерируемые тогда sumбудет быстрым, однако в течение длительных итерируемых только в itertools.chain.from_iterable, iteration_utilities.deepflattenили вложенные пониманиях имеют достаточную производительность при itertools.chain.from_iterableсамом быстром (как уже заметил Нико Schlömer).

from itertools import chain
from functools import reduce
from collections import Iterable  # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten

def nested_list_comprehension(lsts):
    return [item for sublist in lsts for item in sublist]

def itertools_chain_from_iterable(lsts):
    return list(chain.from_iterable(lsts))

def pythons_sum(lsts):
    return sum(lsts, [])

def reduce_add(lsts):
    return reduce(lambda x, y: x + y, lsts)

def pylangs_flatten(lsts):
    return list(flatten(lsts))

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

def reduce_concat(lsts):
    return reduce(operator.concat, lsts)

def iteration_utilities_deepflatten(lsts):
    return list(deepflatten(lsts, depth=1))


from simple_benchmark import benchmark

b = benchmark(
    [nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
     pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
    arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
    argument_name='number of inner lists'
)

b.plot()

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

50
18.04.2018 21:27:43
sumбольше не работает на произвольных последовательностей , как это начинается с 0, что делает functools.reduce(operator.add, sequences)замену (не мы рады , что они удалены reduceиз встроенных команд?). Когда типы известны, это может быть быстрее для использования type.__add__.
Yann Vernier 14.05.2018 06:29:00
@YannVernier Спасибо за информацию. Я думал, что я провел эти тесты на Python 3.6, и он работал с sum. Вы случайно не знаете, на каких версиях Python он перестал работать?
MSeifert 15.05.2018 09:24:44
Я был несколько ошибочным. 0это просто начальное значение по умолчанию, поэтому оно работает, если кто-либо использует аргумент start для запуска с пустым списком ... но это все же строки особых случаев и говорит мне использовать join. Это реализует foldlвместо foldl1. Та же проблема всплывает в 2.7.
Yann Vernier 15.05.2018 09:31:10

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

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

Выполнено! Конечно, вы можете превратить его обратно в список, выполнив list (l)

5
28.11.2016 21:09:09
Это неправильно, flatten уменьшит размеры массива nd до одного, но не объединит списки внутри как один.
Ando Jurai 30.06.2017 08:15:10

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

Код

#from typing import Iterable 
from collections import Iterable                            # < py38


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

Примечания :

демонстрация

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst))                                         # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

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

Ссылка

  • Это решение модифицировано по рецепту Бизли, Д. и Б. Джонса. Рецепт 4.14, поваренная книга Python, 3-е издание, O'Reilly Media Inc., Севастополь, Калифорния: 2013.
  • Нашел более ранний пост SO , возможно, оригинальную демонстрацию.
108
12.06.2019 23:16:42
Я просто написал примерно то же самое, потому что я не видел вашего решения ... вот что я искал, чтобы "рекурсивно сгладить полные множественные списки" ... (+1)
Martin Thoma 25.03.2017 15:32:05
@MartinThoma Очень ценится. К вашему сведению, если для вас обычным делом является выравнивание вложенных итераций, есть некоторые сторонние пакеты, которые хорошо справляются с этой задачей. Это может спасти от изобретения колеса. Я упомянул more_itertoolsсреди других обсуждаемых в этом посте. Приветствия.
pylang 25.03.2017 17:51:51
Возможно, traverseтакже может быть хорошим названием для этого способа дерева, тогда как я бы оставил его менее универсальным для этого ответа, придерживаясь вложенных списков.
Wolf 15.06.2017 10:22:27
Вы можете проверить if hasattr(x, '__iter__')вместо импорта / проверки, Iterableи это также исключит строки.
Ryan Allen 30.04.2018 16:46:07
приведенный выше код не работает, если в одном из вложенных списков есть список строк. [1, 2, [3, 4], [4], [], 9, 9,5, 'ssssss', ['str', 'sss', 'ss'], [3, 4, 5]] вывод: - [1, 2, 3, 4, 4, 9, 9,5, 'ssssss', 3, 4, 5]
sunnyX 12.06.2019 21:35:28

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

> pip install more_itertools

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

import more_itertools


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( источник , предоставленный abarnet).

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
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]
27
13.07.2018 21:06:00
Верно. Это должен быть принятый ответ
brunetton 17.12.2019 14:30:44
Если вы можете позволить себе добавить пакет в свой проект - этот ответ лучше всего
viddik13 5.03.2020 15:53:44

Простой код для 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
5
25.03.2017 05:09:57
Точно так же вы можете использовать pydash . Я считаю, что эта версия гораздо более читабельна, чем понимание списка или любые другие ответы.
gliemezis 6.06.2017 03:22:40
Это супер медленно.
Nico Schlömer 26.07.2017 09:52:16
Почему у него есть модуль с именем _? Это похоже на плохое имя. См stackoverflow.com/a/5893946/6605826
EL_DON 20.07.2018 18:04:35
@EL_DON: со страницы readme на underscore.py «Underscore.py - это порт на python с превосходной библиотекой javascript underscore.js». Я думаю, что это причина для этого имени. И да, это нехорошее имя для питона
Vu Anh 21.07.2018 02:26:20

Следующее кажется мне самым простым:

>>> 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]
11
5.07.2017 05:14:38
Не работает для списков с разными размерами. -1
nurub 20.01.2020 16:33:52

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

functools.reduce(operator.iconcat, a, [])

быть самым быстрым решением. ( operator.iaddодинаково быстро.)

введите описание изображения здесь


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

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 functools_reduce_iconcat(a):
    return functools.reduce(operator.iconcat, 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: [list(range(10))] * n,
    kernels=[
        forfor, sum_brackets, functools_reduce, functools_reduce_iconcat,
        itertools_chain, numpy_flat, numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    xlabel='num lists'
    )
424
5.12.2019 12:21:11
Для огромных вложенных списков «list (numpy.array (a) .flat)» является самым быстрым среди всех функций, перечисленных выше.
Sara 20.01.2019 13:57:20
Пробовал с помощью регулярных выражений: 'list (map (int, re.findall (r "[\ w] +", str (a))))'. Скорость немного медленнее, чем numpy_concatenate
Justas 14.10.2019 08:05:22
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
5
8.08.2017 14:59:07
Сбой для python2.7 для примера вложенного списка в вопросе:[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
EL_DON 22.04.2019 21:34:17
@EL_DON протестирован на python 2.7.5. это отлично работает
englealuze 25.06.2019 14:13:18

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

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']
1
21.09.2017 19:54:13
Да, это работает, но постоянная конкатенация к временному списку, а затем возврат временных списков и добавление этих временных списков к другим временным спискам, другим возвращаемым значениям и т. Д., Выполняется медленно ... Вместо этого следует использовать Generators.
Mitch McMabers 7.11.2019 06:56:44
@MitchMcMabers вы можете предоставить редактирование, если у вас есть лучшее решение. Я не стремился создать что-то эффективное в то время. Просто то, что будет работать. Если ваши данные имеют много (~ 1k +) вложенных списков, то я думаю, что проблема заключается совсем в другом.
Jon 7.11.2019 18:13:34

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

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
19.10.2017 05:46:09
Это не поддерживает итеративные коллекции, кроме списков. Возможно, вы захотите использовать isinstance (temp, Iterable), как некоторые другие примеры. Я думаю, вы также можете немного упростить это, если вы добавляете alist к templist в начале, вам нужен только вложенный цикл while. Вы также можете использовать структуру данных очереди, чтобы избежать полного изменения списка в конце.
Some Java Programmer 18.04.2018 14:05:44
Это старый, но я отвечу на комментарий. Вопрос просит список, а не коллекции. Хотя может существовать некоторая версия этого, которая могла бы избежать внешнего цикла, это не будет простым изменением, поскольку эта версия требует перезаписи временного списка после сглаживания вложенных массивов. И хотя вы могли бы использовать очередь для этого, при использовании реверсирования потери производительности практически отсутствуют, потому что это python, а реверс - просто обратный итератор.
FredMan 23.12.2019 14:31:23

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

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]
3
13.09.2018 11:34:20
Это просто более сложный и немного более медленный способ того, что уже было опубликовано в 3000 фунтов стерлингов. Вчера я заново изобрел его предложение, поэтому в наши дни этот подход кажется довольно популярным;)
Darkonaut 10.01.2018 22:03:32
Не совсем: 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]
tharndt 11.01.2018 08:17:10
мой код как один лайнер будет: flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
tharndt 11.01.2018 08:32:18
Вы действительно правы +1, предложение 0003000 не будет работать с многозначными числами, я также не проверял это раньше, хотя это должно быть очевидно. Вы могли бы упростить свой код и писать [int(e.strip('[ ]')) for e in str(deep_list).split(',')]. Но я бы предложил придерживаться предложения Deleet для реальных случаев использования. Он не содержит хакерских преобразований типов, он быстрее и более универсален, потому что он, естественно, также обрабатывает списки со смешанными типами.
Darkonaut 11.01.2018 16:31:09
К сожалению нет. Но я недавно видел этот код здесь: Книга Практики Python 6.1.2
tharndt 15.01.2018 08:18:01

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

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

На моем компьютере, в Python 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)
1
31.01.2018 09:52:50

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]

Это в 18 раз быстрее, чем подчеркивание ._. Flatten:

Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636
15
20.07.2018 18:16:43

Примечание : ниже относится к Python 3.3+, потому что он использует yield_from. sixэто также сторонний пакет, хотя и стабильный. С другой стороны, вы могли бы использовать sys.version.


В случае 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', является технически итерацию (все strс есть). Однако, читая немного между строк, вы не хотите рассматривать это как таковой - вы хотите рассматривать это как отдельный элемент.
  • Последний элемент [8, [9, 10]]сам по себе является вложенным итерируемым. Базовое понимание списка и chain.from_iterableтолько извлечение "1 уровень вниз".

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

>>> from collections import Iterable
>>> from six import string_types

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


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

Здесь вы проверяете, что подэлемент (1) итерируем с IterableABC itertools, но также хотите убедиться, что (2) элемент не является «строковым».

5
19.06.2018 19:38:25
Если вы все еще заинтересованы в совместимости с Python 2, перейдите yield fromна forцикл, напримерfor x in flatten(i): yield x
pylang 19.06.2018 19:06:43

Возможно, это не самый эффективный способ, но я подумал о том, чтобы поставить одну линию (на самом деле две линии). Обе версии будут работать с вложенными списками произвольной иерархии и использовать языковые функции (Python3.5) и рекурсию.

def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

Выход

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Это работает в глубине в первую очередь. Рекурсия снижается до тех пор, пока не находит элемент не из списка, затем расширяет локальную переменную flistи затем откатывает ее до родительского элемента . Всякий раз, когда flistвозвращается, он распространяется на родителей flistв понимании списка. Поэтому в корне возвращается плоский список.

Вышеупомянутый создает несколько локальных списков и возвращает их, которые используются для расширения списка родителей. Я думаю, что путь к этому может быть создание gloabl flist, как показано ниже.

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

Выход снова

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Хотя в настоящее время я не уверен в эффективности.

4
20.05.2018 08:47:32
Зачем расширять ([l]) вместо добавления (l)?
Maciek 9.04.2020 18:31:12
flat_list = []
for i in list_of_list:
    flat_list+=i

Этот Кодекс также работает хорошо, поскольку он просто расширяет список полностью. Хотя это очень похоже, но есть только один цикл. Так что это сложнее, чем добавление 2 для циклов.

5
20.06.2018 11:12:25

Вы можете использовать NumPy:
flat_list = list(np.concatenate(list_of_list))

6
24.07.2018 09:11:26
Это работает для числовых, строк и смешанных списков
Nitin 19.09.2018 07:53:11
Сбой для неравномерно вложенных данных, таких как[1, 2, [3], [[4]], [5, [6]]]
EL_DON 22.04.2019 21:32:02

Простой рекурсивный метод с использованием reducefrom functoolsи addоператора в списках:

>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Функция flattenпринимает в lstкачестве параметра. Это петли всех элементов lstдо тех пор , достигающие целые числа (не могут также изменить , intчтобы float, strи т.д. для других типов данных), которые добавли ют к возвращаемому значению внешней рекурсии.

В отличие от таких методов, как forциклы и монады, рекурсия заключается в том, что это общее решение, не ограниченное глубиной списка . Например, список с глубиной 5 может быть сведен так же, как l:

>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]
2
20.09.2018 04:55:19
Грустно это говорить (я не люблю понизить ответы), но не используйте этот ответ в своих проектах. Он страдает от слишком глубокой рекурсии RecursionError: maximum recursion depth exceeded while calling a Python objectи работает медленно. Пример входных данных , что не удается: flatten([ [1,2,3], 4, [5, 6], "foo" ]). Вместо этого следует использовать генераторы.
Mitch McMabers 7.11.2019 07:03:12