it-swarm.com.ru

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

Интересно, есть ли ярлык для создания простого списка из списка списков в 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'
2423
Emma

Приведен список списков l,

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.

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

3548
Alex Martelli

Вы можете использовать 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
1222
Shawn Chin

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

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

709
Triptych

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

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

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

 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 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)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )
227
Nico Schlömer
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]
121
Greg Hewgill

Вот общий подход, который применяется к спискам numbers, strings, nested и контейнерам mixed.

Код

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

Ссылка

  • Это решение изменено по рецепту в Бизли, Д. и Б. Джонс. Рецепт 4.14, 3-я редакция Python Cookbook, O'Reilly Media Inc., Севастополь, Калифорния: 2013.
  • Нашел более раннюю SO запись , возможно, оригинальную демонстрацию.
72
pylang

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

>>> 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
34
Nadia Alramli

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

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

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

 enter image description here

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

Результаты показывают, что если итерируемое содержит только несколько внутренних итераций, то 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 Отказ от ответственности: я автор этой библиотеки

31
MSeifert

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

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

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

28
Andrea Ambu

Кажется, есть путаница с operator.add! Когда вы добавляете два списка вместе, правильным термином для этого является concat, а не add. 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)

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

>>> 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 довольно быстрый! Но это не сравнить, чтобы уменьшить с Конкат.

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

Рассмотрите возможность установки пакета 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 ( source , предоставленного 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]
16
pylang

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

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

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

16
Igor Krivokon
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]
11
Anil

Плохая особенность функции 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]
10
Deleet

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

>>> 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]
9
devil in the detail

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

Принятый ответ не работал для меня при работе с текстовыми списками переменной длины. Вот альтернативный подход, который работал для меня.

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

Принятый ответ, который сделал не / работает:

flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']

Новое предлагаемое решение, которое сделал работает для меня:

flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']
7
user9074332

Также можно использовать NumPy's flat :

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

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

7
mdh

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

5
A. Attia

Простой код для underscore.py package fan

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
4
Vu Anh
def flatten(alist):
    if alist == []:
        return []
    Elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
4
englealuze

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

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(',')]
4
tharndt

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

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

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

3
Canuck
flat_list = []
for i in list_of_list:
    flat_list+=i

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

3
Deepak Yadav

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

3
mkultra

Простой рекурсивный метод, использующий reduce из 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
hash_purple

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

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

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

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

2
phoxis

Примечание: ниже применяется к 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', is технически повторяем (все strs). Однако, читая немного между строк, вы не хотите рассматривать это как таковой - вы хотите рассматривать это как отдельный элемент.
  • Последний элемент [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) элемент является not "строковым. "

2
Brad Solomon

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

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

Рекурсивная версия

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

def flatten_list(k):
    result = list()
    for i in k:
        if isinstance(i,list):

            #The isinstance() function checks if the object (first argument) is an 
            #instance or subclass of classinfo class (second argument)

            result.extend(flatten_list(i)) #Recursive call
        else:
            result.append(i)
    return result

flatten_list(x)
#result = [1,2,3,4,5,6,7,8,9,10]
1
Saurabh Singh

Кидаю шляпу в кольцо ...

B = [ [...], [...], ... ]
A = []
for i in B:
  A.extend(i)
1
donlan

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

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

Мое решение (интуитивно понятное и краткое):

def unnest(lists):
    unnested = []
    for l in lists:
        unnested = unnested + l
    return unnested
0
Manrique

Вы можете использовать Dask, чтобы сгладить/объединить список с дополнительным преимуществом сохранения памяти. Dask использует задержанную память, которая активируется только с помощью compute (). Таким образом, вы можете использовать Dask Bag API в своем списке, а затем вычислить () в конце. Документация: http://docs.dask.org/en/latest/bag-api.html

import dask.bag as db

my_list = [[1,2,3],[4,5,6],[7,8,9]]
my_list = db.from_sequence(my_list, npartitions = 1)
my_list = my_list.flatten().compute()

# [1,2,3,4,5,6,7,8,9]
0
N.Bell

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

вы можете использовать класс цепи

chain.from_iterable(['ABC', 'DEF'])

вывод будет: [A B C D E F]

Ссылка: https://docs.python.org/2/library/itertools.html#itertools.chain

0
Balamurugan

Это работает с произвольно вложенными списками. Его можно легко расширить для работы с другими типами итераций.

def flatten(seq):
    """list -> list                                                                                                                                                                           
    return a flattend list from an abitrarily nested list                                                                                                                                     
    """
    if not seq:
        return seq
    if not isinstance(seq[0], list):
        return [seq[0]] + flatten(seq[1:])
    return flatten(seq[0]) + flatten(seq[1:])

Пробный прогон

>>> flatten([1, [2, 3], [[[4, 5, 6], 7], [[8]]], 9])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
0
Xero Smith

Мы можем сделать то же самое в, используя основные понятия Python 

nested_list=[10,20,[30,40,[50]],[80,[10,[20]],90],60]
flat_list=[]
def unpack(list1):
    for item in list1:
        try:
            len(item)
            unpack(item)
        except:
            flat_list.append(item)
unpack(nested_list)
print (flat_list)
0
abhi krishnan