Python: извлечение всех элементов в многоуровневых (неизвестных порядках) вложенных списках [duplicate]

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

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

Код

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'
2095
задан dreftymac 11 October 2017 в 07:04
поделиться

30 ответов

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

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

3018
ответ дан Mona Jalal 15 August 2018 в 22:14
поделиться
  • 1
    Я попробовал тест с теми же данными, используя 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 October 2010 в 02:21
  • 2
    Я понял, что синтаксис трудно понять, пока я не понял, что вы можете думать о нем точно так же, как вложенные для циклов. для подписок в l: для элемента в подсписке: элемент дохода – Rob Crowell 27 July 2011 в 17:43
  • 3
    @BorisChervenkov: Обратите внимание, что я завернул вызов в list(), чтобы реализовать итератор в списке. – intuited 20 May 2012 в 23:56
  • 4
    [лист для дерева в лесу для листьев в дереве] может быть проще понять и применить. – John Mee 29 August 2013 в 02:38
  • 5
    @Joel, на самом деле в настоящее время list(itertools.chain.from_iterable(l)) лучше всего - как замечено в других комментариях и ответе Шона. – Alex Martelli 4 January 2015 в 16:40

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

1
ответ дан A. Attia 15 August 2018 в 22:14
поделиться

Это можно сделать, используя 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

На моем компьютере, в 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)
0
ответ дан bli 15 August 2018 в 22:14
поделиться

Примечание. Ниже применимо к 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 s). Тем не менее, чтение между строками немного, вы не хотите рассматривать его как таковое - вы хотите рассматривать его как один элемент.
  • Последний элемент [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) повторяется с помощью Iterable , ABC из itertools, но также должен гарантировать, что (2) элемент не "string-like . "

2
ответ дан Brad Solomon 15 August 2018 в 22:14
поделиться
  • 1
    Если вы все еще заинтересованы в совместимости с Python 2, измените yield from на цикл for, например. for x in flatten(i): yield x – pylang 19 June 2018 в 19:06

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

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

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

2
ответ дан Canuck 15 August 2018 в 22:14
поделиться
  • 1
    Это неправильно, сглаживание уменьшит размеры массива nd до одного, но не объединит списки внутри как один. – Ando Jurai 30 June 2017 в 08:15
flat_list = []
for i in list_of_list:
    flat_list+=i

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

2
ответ дан Deepak Yadav 15 August 2018 в 22:14
поделиться

Плохая особенность функции 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]
8
ответ дан Deleet 15 August 2018 в 22:14
поделиться

Мне кажется, что мне проще:

>>> 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]
7
ответ дан devil in the detail 15 August 2018 в 22:14
поделиться
  • 1
    не работает с уже плоскими массивами, рассмотрим: import numpy as np l = [1, 2, 3] print (np.concatenate(l)) ValueError: zero-dimensional arrays cannot be concatenated – Stas Bekman 23 June 2018 в 20:51
  • 2
    Я не уверен, чего вы достигнете, опуская комментарий, который идентифицирует недостаток в вашем ответе. Система голосования предназначена для того, чтобы помочь другим сэкономить свое время, используя ответы, которые работают. Ваш работает только частично, как указано выше, и делает предположение о том, что вход не является плоским и не обобщает. – Stas Bekman 25 June 2018 в 16:58

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

>>> 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
31
ответ дан devsaw 15 August 2018 в 22:14
поделиться
  • 1
    для действительно миниатюрного списка, например. один с 3 подсписками, может быть, но так как производительность суммы идет с O (N ** 2), в то время как понимание списка идет с O (N), только увеличение списка входных данных немного изменит ситуацию - действительно, LC будет , бесконечно быстрее " чем сумма на пределе по мере роста N. Я отвечал за разработку суммы и выполнение ее первой реализации в среде исполнения Python, и мне все же хотелось бы, чтобы я нашел способ эффективно ограничивать ее суммированием чисел (что действительно хорошо) и блокировать «привлекательную неприятность». он предлагает людям, которые хотят «суммировать». списки ;-). – Alex Martelli 4 June 2009 в 22:07

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
5
ответ дан EL_DON 15 August 2018 в 22:14
поделиться
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
4
ответ дан englealuze 15 August 2018 в 22:14
поделиться

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

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
ответ дан FredMan 15 August 2018 в 22:14
поделиться
  • 1
    Это не поддерживает итерируемые коллекции, отличные от списков. Возможно, вы захотите рассмотреть использование isinstance (temp, Iterable), как и некоторые другие примеры. Я думаю, вы также можете немного упростить это, если вы добавите alist templist в начале, вам нужно будет только вложенный цикл while. Вы также можете использовать структуру данных очереди, чтобы избежать изменения всего списка в конце. – Some Java Programmer 18 April 2018 в 14:05
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]
5
ответ дан Guillaume Jacquenot 15 August 2018 в 22:14
поделиться
  • 1
    Не работает для меня, если я вручную не укажу 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] – Jeff 6 November 2016 в 23:52
  • 2
    @Jeff Мой ответ редактировался @ deleet ... Проверьте мой оригинальный ответ, и он работает ... – Anil 8 November 2016 в 04:04
  • 3
    хорошо, спасибо. Я откатился @ редактировать deleet – Jeff 8 November 2016 в 19:21
  • 4
    Да, это было плохо, как я узнал позже! Я проверил его, но ошибка произошла только после первого запуска. Причина в том, что аргумент по умолчанию [] рассматривается как согласованный объект в Python. Поэтому в следующий раз, когда вы запустите функцию, она начинается с списка, который вы использовали в прошлый раз! Очень неприятная ошибка, трудно понять. В R (который я использую в основном) это сработало бы из-за семантики копирования. Кто-нибудь знает решение Python? При необходимости вручную указывать пустой список каждый раз не является хорошим дизайном. Мне нужна эта функция для моего собственного проекта, поэтому я надеюсь, что кто-то знает. :) – Deleet 9 November 2016 в 04:27
  • 5
    Я отправил исправленную версию сейчас. – Deleet 11 November 2016 в 12:53

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

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

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

13
ответ дан Igor Krivokon 15 August 2018 в 22:14
поделиться
  • 1
    extend лучше использовать как newlist = [], extend = newlist.extend, for sublist in l: extend(l), поскольку он избегает (довольно больших) служебных данных lambda, поиска атрибутов на x и or. – agf 24 September 2011 в 11:12

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

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']
2
ответ дан Jon 15 August 2018 в 22:14
поделиться

Очищено пример @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

0
ответ дан Kevin Postal 15 August 2018 в 22:14
поделиться
  • 1
    Спасибо за подход. У меня есть один комментарий к вашему ключевому слову a. Mutable default args имеют побочные эффекты и их часто избегают в Python. У этой ссылки есть решение. Приветствия. – pylang 25 March 2017 в 19:22

Можно также использовать FlatPage flatp :

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

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

6
ответ дан mdh 15 August 2018 в 22:14
поделиться

Кажется, что с 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]

Aha, вы получите список.

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

>>> 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
19
ответ дан Meitham 15 August 2018 в 22:14
поделиться
  • 1
    Хм, чтобы быть справедливым вторым примером, должен быть также список (или первый кортеж?) – Mr_and_Mrs_D 28 May 2017 в 13:20

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

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

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

25
ответ дан MERose 15 August 2018 в 22:14
поделиться

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

3
ответ дан mkultra 15 August 2018 в 22:14
поделиться

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

>>> 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]

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

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

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

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 Отказ от ответственности: я являюсь автором этой библиотеки

17
ответ дан MSeifert 15 August 2018 в 22:14
поделиться
  • 1
    sum больше не работает с произвольными последовательностями, начиная с 0, делая functools.reduce(operator.add, sequences) заменой (мы не рады, что они удалили reduce из встроенных?). Когда типы известны, возможно, быстрее использовать type.__add__. – Yann Vernier 14 May 2018 в 06:29
  • 2
    @YannVernier Спасибо за информацию. Я думал, что я запускал эти тесты на Python 3.6, и он работал с sum. Вы знаете, какие версии Python перестали работать? – MSeifert 15 May 2018 в 09:24
  • 3
    Я несколько ошибся. 0 - это только начальное значение по умолчанию, поэтому оно работает, если вы используете начальный аргумент start , чтобы начать с пустого списка ... но он все еще содержит специальные строки и говорит мне использовать join. Он реализует foldl вместо foldl1. Эта же проблема появляется в версии 2.7. – Yann Vernier 15 May 2018 в 09:31

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

list(itertools.chain.from_iterable(a))

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


Код для воспроизведения графика:

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: [list(range(10))] * n,
    kernels=[
        forfor, sum_brackets, functools_reduce, itertools_chain, numpy_flat,
        numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )
135
ответ дан Nico Schlömer 15 August 2018 в 22:14
поделиться

Это может быть не самый эффективный способ, но я решил поставить однострочный (фактически двухстрочный). Обе версии будут работать с произвольными иерархическими вложенными списками и будут использовать языковые возможности (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]

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

1
ответ дан phoxis 15 August 2018 в 22:14
поделиться

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

> pip install more_itertools

Он поставляется с реализацией для flatten ( source из рецептов 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]
55
ответ дан pylang 15 August 2018 в 22:14
поделиться
  • 1
    Я просто написал примерно то же самое, потому что я не видел вашего решения ... вот что я искал и «рекурсивно сплющил полные множественные списки». ... (+1) – Martin Thoma 25 March 2017 в 16:32
  • 2
    @MartinThoma Очень ценится. FYI, если выравнивание вложенных итераций является обычной практикой для вас, есть некоторые сторонние пакеты, которые хорошо справляются с этим. Это может спастись от повторного использования колеса. Я упомянул more_itertools среди других, обсуждавшихся в этом сообщении. Приветствия. – pylang 25 March 2017 в 18:51
  • 3
    Nice - просто интересовался конструкцией типа yield from на python, узнав о yield * в es2015. – Triptych 14 April 2017 в 21:30
  • 4
    замените на if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)): на поддержку строк. – Jorge Leitão 13 May 2017 в 23:40
  • 5
    Верный. Исходный рецепт поваренной книги фактически показывает, как поддерживать строки и байты. Если они отредактировали его, чтобы отразить эту поддержку. – pylang 14 May 2017 в 00:25
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 be

>>> 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]
101
ответ дан Sanjay Verma 15 August 2018 в 22:14
поделиться
  • 1
    reduce(operator.add, l) будет правильным способом сделать версию reduce. Встроенные функции быстрее, чем lambdas. – agf 24 September 2011 в 11:04
  • 2
    @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 21 March 2012 в 00:13
  • 3
    Это Шлемиэль, алгоритм художника joelonsoftware.com/articles/fog0000000319.html – Mike Graham 25 April 2012 в 19:26
  • 4
    это может использоваться только для integers. Но что, если список содержит string? – Freddy 11 September 2015 в 07:16
  • 5
    @Freddy: Функция operator.add работает одинаково хорошо для обоих списков целых чисел и списков строк. – Greg Hewgill 11 September 2015 в 07:38

Вы можете использовать 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], и, похоже, работает быстрее:

[me@home]$ 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
[me@home]$ 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
[me@home]$ 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
[me@home]$ 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
[me@home]$ python --version
Python 2.7.3
1087
ответ дан Shawn Chin 15 August 2018 в 22:14
поделиться
  • 1
    @ShawnChin BTW, часть оборудования, которое у вас было при ответе на этот вопрос, моя текущая рабочая станция вдвое быстрее и ей исполнилось 4 года. – Manuel Gutierrez 24 September 2013 в 16:18
  • 2
  • 3
    * - сложная вещь, которая делает chain менее понятным, чем понимание списка. Вы должны знать, что цепочка соединяется вместе только итерами, передаваемыми как параметры, и * заставляет список верхнего уровня расширяться в параметры, поэтому chain объединяет все эти итерации, но не спускается дальше. Я думаю, что это делает понимание более читаемым, чем использование цепочки в этом случае. – Tim Dierks 3 September 2014 в 15:13
  • 4
    @TimDierks: я не уверен, что «это требует от вас понимания синтаксиса Python». является аргументом против использования данной методики в Python. Конечно, сложное использование может запутать, но «splat» оператор, как правило, полезен во многих случаях, и это не использует его особенно неясным образом; отвергая все языковые функции, которые не обязательно очевидны для начинающих пользователей, означает, что вы связываете одну руку за спиной. Может также выкинуть список понятий, пока вы на нем; пользователи из другого фона найдут цикл for, который несколько раз append станет более очевидным. – ShadowRanger 12 November 2015 в 21:26
  • 5
    как насчет ['abcde_', ['_abcde', ['e_abcd', ['de_abc', ['cde_ab', ['bcde_a']]]]]] – Aymon Fournier 7 December 2015 в 20:24

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

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]

1
ответ дан tharndt 15 August 2018 в 22:14
поделиться
  • 1
    Это всего лишь более сложный и немного более медленный способ того, что уже было опубликовано ранее. Я заново изобрел его предложение вчера, поэтому этот подход кажется довольно популярным в наши дни;) – Darkonaut 10 January 2018 в 23:03
  • 2
    Не совсем: wierd_list = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10] & gt; & gt; nice_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0] – tharndt 11 January 2018 в 09:17
  • 3
    мой код как один лайнер будет: flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')] – tharndt 11 January 2018 в 09:32
  • 4
    Вы действительно правы +1, предложение ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000 не будет работать с несколькими номерами цифр, я также не проверял это раньше, хотя это должно быть очевидно. Вы можете упростить свой код и написать [int(e.strip('[ ]')) for e in str(deep_list).split(',')]. Но я предлагаю придерживаться предложения Деле для реальных случаев использования. Он не содержит преобразований хакерского типа, он быстрее и универсален, потому что он, естественно, также обрабатывает списки со смешанными типами. – Darkonaut 11 January 2018 в 17:31
  • 5
    К сожалению нет. Но я недавно увидел этот код: Практическая книга Питона 6.1.2 – tharndt 15 January 2018 в 09:18

Примечание автора: Это неэффективно. Но весело, потому что монады потрясающие. Это не подходит для производственного кода 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].

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

639
ответ дан Triptych 15 August 2018 в 22:14
поделиться
  • 1
    это довольно аккуратно и умно, но я бы не использовал его, потому что это путано читать. – andrewrk 15 June 2010 в 19:55
  • 2
    Это Shlemiel алгоритм художника joelonsoftware.com/articles/fog0000000319.html - излишне неэффективный, а также излишне уродливый. – Mike Graham 25 April 2012 в 19:24
  • 3
    Операция append в списках формирует Monoid , что является одной из самых удобных абстракций для мышления о работе + в общем смысле (не ограничиваясь числами только). Поэтому этот ответ заслуживает +1 от меня за (правильное) обращение с списками как моноид. Производительность имеет отношение, хотя ... – ulidtko 3 December 2014 в 11:35
  • 4
    @andrewrk Ну, некоторые люди думают, что это самый чистый способ сделать это: youtube.com/watch?v=IOiZatlZtGU те, кто не понимает, почему это круто, просто нужно подождать несколько десятилетий пока все не сделают это так: давайте будем использовать языки программирования (и абстракции), которые будут обнаружены и не изобретены, обнаружен Monoid. – jhegedus 5 October 2015 в 08:51
  • 5
    это очень неэффективный путь из-за квадратичного аспекта суммы. – Jean-François Fabre 31 July 2017 в 18:04

Простой код для вентилятора пакета 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

pip install underscore.py
5
ответ дан Vu Anh 15 August 2018 в 22:14
поделиться
  • 1
    Аналогично, вы можете использовать pydash . Я считаю эту версию более читаемой, чем понимание списка или любые другие ответы. – gliemezis 6 June 2017 в 03:22
  • 2
    Это очень медленно. – Nico Schlömer 26 July 2017 в 09:52
  • 3
    Почему у него есть модуль с именем _? Это похоже на плохое имя. См. stackoverflow.com/a/5893946/6605826 – EL_DON 20 July 2018 в 18:04
  • 4
    @EL_DON: С страницы underscore.py readme & quot; Underscore.py - это порт python отличной библиотеки javascript underscore.js & quot ;. Я думаю, что это причина этого имени. И да, это нехорошее имя для python – Vu Anh 21 July 2018 в 02:26

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

Код

from collections import Iterable


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

Примечание: в Python 3, yield from flatten(x) может заменить for sub_x in flatten(x): yield sub_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']

Ссылка

  • Это решение модифицировано из рецепта в Beazley, D. and B. Джонс. Рецепт 4.14, Поваренная книга Python 3rd Ed., O'Reilly Media Inc. Севастополь, Калифорния: 2013.
  • Найден ранее SO post , возможно, оригинальная демонстрация.
55
ответ дан pylang 15 August 2018 в 22:14
поделиться
  • 1
    Я просто написал примерно то же самое, потому что я не видел вашего решения ... вот что я искал и «рекурсивно сплющил полные множественные списки». ... (+1) – Martin Thoma 25 March 2017 в 16:32
  • 2
    @MartinThoma Очень ценится. FYI, если выравнивание вложенных итераций является обычной практикой для вас, есть некоторые сторонние пакеты, которые хорошо справляются с этим. Это может спастись от повторного использования колеса. Я упомянул more_itertools среди других, обсуждавшихся в этом сообщении. Приветствия. – pylang 25 March 2017 в 18:51
  • 3
    Nice - просто интересовался конструкцией типа yield from на python, узнав о yield * в es2015. – Triptych 14 April 2017 в 21:30
  • 4
    замените на if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)): на поддержку строк. – Jorge Leitão 13 May 2017 в 23:40
  • 5
    Верный. Исходный рецепт поваренной книги фактически показывает, как поддерживать строки и байты. Если они отредактировали его, чтобы отразить эту поддержку. – pylang 14 May 2017 в 00:25
Другие вопросы по тегам:

Похожие вопросы: