web-dev-qa-db-fra.com

Comment faire une liste à plat d'une liste de listes?

Je me demande s'il existe un raccourci pour créer une liste simple à partir d'une liste de listes en Python.

Je peux le faire en boucle perdue, mais peut-être existe-t-il un "one-liner"? Je l'ai essayé avec reduction, mais j'ai une erreur.

Code

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

Message d'erreur

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

Étant donné une liste de listes l,

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

ce qui signifie:

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

est plus rapide que les raccourcis affichés jusqu'à présent. (l est la liste à aplatir.)

Voici la fonction correspondante:

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

Pour preuve, vous pouvez utiliser le module timeit dans la bibliothèque standard:

$ 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

Explication: les raccourcis basés sur + (y compris l'utilisation implicite dans sum) sont nécessairement O(L**2) lorsqu'il existe plusieurs sous-listes - car la liste des résultats intermédiaires s'allonge, à chaque étape, un nouvel objet de liste des résultats intermédiaires est alloué, et tous les éléments du résultat intermédiaire précédent doivent être copiés (ainsi que quelques nouveaux ajoutés à la fin). Donc, pour des raisons de simplicité et sans perte de généralité, supposons que vous ayez une sous-liste de I articles chacun: les premiers articles sont copiés L-1 fois, les deuxièmes articles L-2 fois, etc. Le nombre total de copies correspond à I fois la somme de x pour x de 1 à L exclu, c’est-à-dire, I * (L**2)/2.

La compréhension de liste ne génère qu'une liste, une fois, et copie également chaque élément (de son lieu de résidence d'origine à la liste des résultats) également une fois.

3548
Alex Martelli

Vous pouvez utiliser itertools.chain() :

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

ou, sur Python> = 2.6, utilisez itertools.chain.from_iterable() qui ne nécessite pas de décompresser la liste:

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

Cette approche est sans doute plus lisible que [item for sublist in l for item in sublist] et semble être aussi plus rapide:

[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
1222
Shawn Chin

Note de l'auteur: C'est inefficace. Mais amusant, parce que monoids sont géniaux. Ce n'est pas approprié pour le code Python de production.

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

Ceci additionne simplement les éléments de iterable passés dans le premier argument, le second argument étant considéré comme la valeur initiale de la somme (sinon, 0 est utilisé à la place et ce cas vous donnera une erreur).

Comme vous additionnez des listes imbriquées, vous obtenez [1,3]+[2,4] à la suite de sum([[1,3],[2,4]],[]), qui est égal à [1,3,2,4].

Notez que ne fonctionne que sur des listes de listes. Pour les listes de listes de listes, vous aurez besoin d'une autre solution.

709
Triptych

J'ai testé la plupart des solutions suggérées avec perfplot (un de mes projets favoris, essentiellement un wrapper autour de timeit)

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

être la solution la plus rapide. (operator.iadd est également rapide.)

 enter image description here


Code pour reproduire l'intrigue:

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]

La méthode extend() de votre exemple modifie x au lieu de renvoyer une valeur utile (ce que reduce() attend).

Un moyen plus rapide de faire la version reduce serait

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

Je prends ma déclaration en arrière. la somme n'est pas le gagnant. Bien que ce soit plus rapide quand la liste est petite. Mais la performance se dégrade de manière significative avec des listes plus grandes.  

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

La version sum fonctionne toujours depuis plus d'une minute et le traitement n'est pas encore terminé!

Pour les listes moyennes:

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

Utilisation de petites listes et timeit: nombre = 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

Si vous voulez aplatir une structure de données où vous ne savez pas à quelle profondeur elle est imbriquée, vous pouvez utiliser 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]

C'est un générateur, vous devez donc transtyper le résultat en une variable list ou l'exécuter explicitement.


Pour n'aplatir qu'un seul niveau et si chacun des éléments est lui-même itératif, vous pouvez également utiliser iteration_utilities.flatten qui n'est lui-même qu'une mince couche autour de 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]

Juste pour ajouter quelques timings (basé sur la réponse de Nico Schlömer qui n'incluait pas la fonction présentée dans cette réponse):

 enter image description here

Il s’agit d’un graphique log-log permettant de prendre en compte la vaste gamme de valeurs couvertes. Pour un raisonnement qualitatif: plus c'est bas, mieux c'est.

Les résultats montrent que si l'iterable ne contient que quelques iterables internes, alors sum sera le plus rapide. Cependant, pour de longues iterables, seuls le itertools.chain.from_iterable, le iteration_utilities.deepflatten ou la compréhension imbriquée ont des performances raisonnables, itertools.chain.from_iterable étant le plus rapide (comme le remarquait déjà Nico 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 Disclaimer: je suis l'auteur de cette bibliothèque

31
MSeifert

Pourquoi utilisez-vous prolonger?

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

Cela devrait bien fonctionner.

28
Andrea Ambu

Il semble y avoir une confusion avec operator.add! Lorsque vous ajoutez deux listes ensemble, le terme correct pour cela est concat, pas ajouter. operator.concat est ce que vous devez utiliser.

Si vous pensez fonctionnel, c'est aussi simple que cela:

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

Vous voyez réduire respecte le type de séquence, donc lorsque vous fournissez un tuple, vous récupérez un tuple. Essayons avec une liste ::

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

Aha, vous obtenez une liste.

Qu'en est-il de la performance ::

>>> 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 est assez rapide! Mais ce n'est pas une comparaison pour réduire avec 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
27
Meitham

Pensez à installer le paquet more_itertools .

> pip install more_itertools

Il est livré avec une implémentation pour flatten ( source , à partir des recettes de 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]

A partir de la version 2.4, vous pouvez aplatir des itérables imbriqués plus complexes et plus compliqués avec more_itertools.collapse ( source , contribué par 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

La raison pour laquelle votre fonction n'a pas fonctionné: la commande extend étend le tableau sur place et ne le renvoie pas Vous pouvez toujours retourner x de lambda, en utilisant une astuce:

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

Remarque: extend est plus efficace que + sur les listes.

16
Igor Krivokon

Ne réinventez pas la roue si vous utilisez Django:

>>> from Django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]

...Pandas:

>>> from pandas.core.common import flatten
>>> list(flatten(l))

...Itertools:

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))

...Matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))

...nipath:

>>> from unipath.path import flatten
>>> list(flatten(l))

...Setuptools:

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
12
Max Malysh
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

Une des mauvaises caractéristiques de la fonction d'Anil ci-dessus est qu'elle oblige l'utilisateur à toujours spécifier manuellement le deuxième argument comme étant une liste vide []. Cela devrait plutôt être un défaut. En raison du fonctionnement des objets Python, ceux-ci doivent être définis dans la fonction et non dans les arguments.

Voici une fonction de travail:

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

Essai:

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

Suivre me semble le plus simple:

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

matplotlib.cbook.flatten() fonctionnera pour les listes imbriquées même si elles imbriquent plus profondément que l'exemple.

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

Résultat:

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

Ceci est 18 fois plus rapide que le trait de soulignement ._. 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

La réponse acceptée ne fonctionnait pas pour moi s’agissant de listes textuelles de longueurs variables. Voici une autre approche qui a fonctionné pour moi.

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

Réponse acceptée qui (pas} _ fonctionnait:

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

Nouvelle solution proposée qui faisait fonctionne pour moi:

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

On peut aussi utiliser NumPy's flat :

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

Éditer 11/02/2016: Ne fonctionne que lorsque les sous-listes ont des dimensions identiques.

7
mdh

Code simple pour 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]

Il résout tous les problèmes d'aplatissement (aucun élément de liste ou imbrication complexe)

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]

Vous pouvez installer underscore.py avec 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

Une autre approche inhabituelle qui fonctionne pour les listes d’entiers hétérogènes et homogènes:

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

La solution la plus rapide que j'ai trouvée (pour les grandes listes quand même):

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

Terminé! Vous pouvez bien sûr le transformer en liste en exécutant list (l)

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

Ce code fonctionne également correctement car il ne fait que rallonger la liste. Bien que ce soit très similaire mais n’en ayez qu’une pour la boucle. Donc, il a moins de complexité que d'ajouter 2 pour les boucles.

3
Deepak Yadav

Si vous êtes prêt à abandonner une petite quantité de vitesse pour un look plus net, vous pouvez utiliser numpy.concatenate().tolist() ou 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

Vous pouvez en savoir plus ici dans la documentation numpy.concatenate et numpy.ravel

3
mkultra

Une méthode récursive simple utilisant reduce de functools et l'opérateur add sur les listes:

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

La fonction flatten prend en paramètre lst. Il boucle tous les éléments de lst jusqu'à atteindre des nombres entiers (peut également changer int en float, str, etc. pour d'autres types de données), qui sont ajoutés à la valeur de retour de la récursivité la plus externe.

La récursivité, contrairement aux méthodes telles que les boucles for et les monades, est qu’il s’agit de une solution générale non limitée par la liste profondeur . Par exemple, une liste de profondeur 5 peut être aplatie de la même manière que 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

Je suis récemment tombé sur une situation où j’avais un mélange de chaînes et de données numériques dans des sous-listes telles que

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

où les méthodes telles que flat_list = [item for sublist in test for item in sublist] n'ont pas fonctionné. Donc, je suis venu avec la solution suivante pour 1+ niveau de sous-listes

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

Et le résultat

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

Ce n'est peut-être pas le moyen le plus efficace, mais j'ai pensé mettre une doublure (en fait une doublure). Les deux versions fonctionneront sur des listes imbriquées dans une hiérarchie arbitraire et exploiteront les fonctionnalités du langage (Python3.5) et la récursivité.

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)

La sortie est

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

Cela fonctionne d'une manière d'abord en profondeur. La récursivité diminue jusqu'à ce qu'elle trouve un élément non-list, puis étend la variable locale flist et la restaure ensuite au parent. Chaque fois que flist est renvoyé, il est étendu à la variable flist du parent dans la liste de compréhension. Par conséquent, à la racine, une liste non hiérarchique est renvoyée.

Le précédent crée plusieurs listes locales et les renvoie, qui servent à étendre la liste du parent. Je pense que la solution pourrait être de créer une flist globale, comme ci-dessous.

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)

La sortie est à nouveau

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

Bien que je ne sois pas sûr pour le moment de l'efficacité.

2
phoxis

Note: La version ci-dessous s'applique à Python 3.3+ car il utilise yield_from . six est également un package tiers, bien qu'il soit stable. Alternativement, vous pouvez utiliser sys.version.


Dans le cas de obj = [[1, 2,], [3, 4], [5, 6]], toutes les solutions présentées ici sont bonnes, y compris la compréhension de liste et itertools.chain.from_iterable.

Cependant, considérons ce cas légèrement plus complexe:

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

Il y a plusieurs problèmes ici:

  • Un élément, 6, est juste un scalaire; ce n'est pas itérable, donc les routes ci-dessus vont échouer ici.
  • Un élément, 'abc', est techniquement itérable (tous les strs sont). Cependant, en lisant un peu entre les lignes, vous ne voulez pas le traiter comme tel, mais plutôt comme un élément unique.
  • L'élément final, [8, [9, 10]], est lui-même un élément imbriqué imbriqué. Compréhension de base des listes et chain.from_iterable n'extrayez que "1 level down".

Vous pouvez y remédier comme suit:

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

Ici, vous vérifiez que le sous-élément (1) est itérable avec Iterable , un ABC de itertools, mais vous voulez également vous assurer que (2) l’élément est non "comme une chaîne. "

2
Brad Solomon

Vous pouvez éviter les appels récursifs à la pile en utilisant assez simplement une structure de données de pile réelle.

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

Version récursive

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

Jeter mon chapeau sur le ring ...

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

Cela peut être fait en utilisant toolz.concat ou cytoolz.concat (version cythonisée, qui pourrait être plus rapide dans certains cas):

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

Sur mon ordinateur, en python 3.6, le temps semble presque aussi rapide que [item for sublist in l for item in sublist] (sans compter le temps d'importation):

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)

La version toolz est en effet plus lente:

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

La question demandait une réponse géniale "one-liner". Ensuite, voici ma contribution en clair Python:

a = [[2,3,6],[False,'foo','bar','baz'],[3.1415],[],[0,0,'0']]
flat_a = [a[i][j] for i in range(len(a)) for j in range(len(a[i]))]
flat_a
[2, 3, 6, False, 'foo', 'bar', 'baz', 3.1415, 0, 0, '0']
0
Ramon

Ma solution (intuitive et courte):

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

Vous pouvez utiliser Dask pour aplatir/fusionner une liste, avec l'avantage supplémentaire de la conservation de la mémoire. Dask utilise la mémoire différée, qui ne s'active qu'avec compute (). Vous pouvez donc utiliser l'API Dask Bag sur votre liste, puis la calculer () à la fin. Documentation: 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

Si vous souhaitez prendre un itinéraire plus lent (durée d'exécution), vous devriez aller plus loin que la vérification par rapport à la chaîne. Vous pourriez avoir une liste imbriquée de nuplets ou un nuplet de listes ou une séquence de types itérables définis par l'utilisateur.

Prenez un type de la séquence d'entrée et vérifiez par rapport à cela.

def iter_items(seq):
    """Yield items in a sequence."""
    input_type = type(seq)

    def items(subsequence):
        if type(subsequence) != input_type:
            yield subsequence
        else:
            for sub in subsequence:
                yield from items(sub)

    yield from items(seq)


>>> list(iter_items([(1, 2), [3, 4], "abc", [5, 6], [[[[[7]]]]]]))
[(1, 2), 3, 4, 'abc', 5, 6, 7]
0
Shay

Exemple nettoyé @ 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))

Exemple: https://repl.it/G8mb/0

0
Kevin Postal

Cela fonctionne avec des listes non imbriquées. Il peut être facilement étendu pour fonctionner avec d'autres types de iterables.

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

Échantillon échantillon

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

Voici une fonction utilisant la récursivité qui fonctionnera sur toute liste imbriquée arbitraire.

def flatten(nested_lst):
    """ Return a list after transforming the inner lists
        so that it's a 1-D list.

    >>> flatten([[[],["a"],"a"],[["ab"],[],"abc"]])
    ['a', 'a', 'ab', 'abc']
    """
    if not isinstance(nested_lst, list):
        return(nested_lst)

    res = []
    for l in nested_lst:
        if not isinstance(l, list):
            res += [l]
        else:
            res += flatten(l)


    return(res)

>>> flatten([[[],["a"],"a"],[["ab"],[],"abc"]])
['a', 'a', 'ab', 'abc']
0
Abhishek Bhatia

une autre façon amusante de faire ceci:

from functools import reduce
from operator import add

li=[[1,2],[3,4]]
x= reduce(add, li)
0
sudeepgupta90

Nous pouvons faire la même chose en utilisant les concepts de base de 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