Question

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

Je peux le faire dans un for boucle, mais peut-être qu'il y a un "one-liner" sympa ?je l'ai essayé avec réduire, mais j'obtiens 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'
Était-ce utile?

La solution

Étant donné une liste de listes l,

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

ce qui veut dire:

flat_list = []
for sublist in l:
    for item in sublist:
        flat_list.append(item)

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

Voici la fonction correspondante:

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

Comme 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, par nécessité, O(L**2) quand il y a des L sous-listes - comme la liste des résultats intermédiaires ne cesse de se plus, à chaque étape un nouvel objet de la liste de résultat intermédiaire obtient attribués, et tous les éléments doivent être copiés dans le résultat intermédiaire précédent sur (ainsi que quelques nouveaux ajoutés à la fin). Donc, pour simplifier et sans perte réelle de généralité, supposons que vous avez L de sous-listes I points chacun: les premiers I éléments sont recopiées et en arrière L-1 fois, la deuxième fois I articles L-2, et ainsi de suite; nombre total de copies est I fois la somme de x pour x compris entre 1 et L exclu, à savoir, I * (L**2)/2.

La compréhension de la liste génère une seule liste, une fois, et des copies de chaque élément sur (de son lieu de résidence d'origine à la liste des résultats) aussi exactement une fois.

Autres conseils

Vous pouvez utiliser itertools.chain() :

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

ou, python> = 2,6, utiliser itertools.chain.from_iterable() qui ne nécessite pas de déballer 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 plus rapide aussi:

[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

Note de l'auteur : Ceci est inefficace. Mais le plaisir, parce que monoids sont impressionnantes. Il ne convient pas pour le code Python de production.

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

Ceci résume simplement les éléments de itérables passé dans le premier argument, le traitement de deuxième argument que la valeur initiale de la somme (si pas donné, 0 est utilisé à la place et ce cas vous donnera une erreur).

Parce que vous sommateur listes imbriquées, vous obtenez en fait [1,3]+[2,4] en raison de sum([[1,3],[2,4]],[]), qui est égale à [1,3,2,4].

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

Je l'ai testé des solutions proposées avec le plus perfplot (un projet de la mienne, essentiellement une enveloppe autour timeit) et trouvé

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

être la solution la plus rapide. (operator.iadd est tout aussi rapide.)

 ici


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'
    )
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() dans votre exemple modifie x au lieu de retourner une valeur utile (ce qui reduce() attend).

Une façon 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]

Voici une approche générale sur nombre , chaînes , imbriqués Listes et mélangés conteneurs .

code

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


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

Remarques :

  • En Python 3, yield from flatten(x) peut remplacer for sub_x in flatten(x): yield sub_x
  • En Python 3.8, les classes de base abstraites déplacé de collection.abc au module de typing.

Démo

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

Référence

  • Cette solution est modifiée à partir d'une recette dans Beazley, D. et B. Jones. Recette 4,14, Python livre de recettes 3e édition, O'Reilly Media Inc. Sebastopol, CA:.. 2013
  • poster SO , peut-être la démonstration originale.

Si vous voulez aplatir une structure de données où vous ne savez pas comment vous pourriez il est profondément imbriqué utiliser 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]

Il est un générateur de sorte que vous devez jeter le résultat à un list ou itérer explicitement dessus.


Pour aplatir un seul niveau et si chacun des éléments est lui-même itérables vous pouvez également utiliser iteration_utilities.flatten qui lui-même est juste une enveloppe mince autour 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]

Il suffit d'ajouter des horaires (fonction de la réponse Nico Schlömer qui ne comprenaient pas la fonction présentée dans cette réponse):

 ici

Il est un terrain log-log pour tenir compte de la vaste gamme de valeurs étalonnés. Pour un raisonnement qualitatif: inférieur est mieux

.

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

Je prends ma déclaration de retour. somme n'est pas le gagnant. Bien qu'il soit plus rapide lorsque la liste est faible. 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 de somme est encore en cours d'exécution pendant plus d'une minute et il n'a pas fait encore le traitement!

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

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, il est aussi facile que cela ::

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

Vous voyez réduire le type de points séquence, donc quand vous donnez un tuple, vous revenez un tuple. Essayons 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 récupérez une liste.

Comment sur 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 il n'y a pas de comparaison à 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

Pourquoi utilisez-vous étendre?

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

Cela devrait bien.

Envisagez d'installer le paquet more_itertools .

> pip install more_itertools

Il est livré avec une mise en œuvre flatten ( , du itertools recettes ):

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]

de la version 2.4, vous pouvez aplatir plus complexes, imbriqués avec iterables la source , a 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]

La raison pour laquelle votre fonction ne fonctionne pas: l'étendue étend tableau en place et ne revient pas. Vous pouvez toujours revenir x de lambda, en utilisant une astuce:

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

Note:. Extend est plus efficace que + sur les listes

Ne pas réinventer 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))

... Unipath :

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

... setuptools :

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

Une mauvaise caractéristique de la fonction Anil ci-dessus est qu'il demande à l'utilisateur de toujours spécifier manuellement le second argument comme une [] liste vide. Cela devrait plutôt être un défaut. En raison de la façon dont les objets Python fonctionnent, ceux-ci doivent être définies dans la fonction, et non pas 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

Test:

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]

matplotlib.cbook.flatten() fonctionnera pour les listes imbriquées même si elles s'emboîtent 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]

C'est 18 fois plus rapide que underscore._.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

La réponse acceptée n'a pas fonctionné pour moi lorsqu'ils traitent avec des listes basées sur des textes de longueurs variables. Voici une autre approche qui a travaillé pour moi.

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

réponse acceptée qui a fait pas travail:

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 que a fait de travail 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']

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]

Après sembler plus simple pour moi:

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

On peut aussi utiliser de NumPy plat :

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

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

.

Vous pouvez utiliser numpy:
flat_list = list(np.concatenate(list_of_list))

Le code simple pour underscore.py fan de package

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 aplatissent (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
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
flat_list = []
for i in list_of_list:
    flat_list+=i

Ce code fonctionne aussi bien comme elle vient étendre la liste tout le chemin. Bien qu'il soit très semblable, mais seulement avoir une boucle. Donc, il a moins de complexité que l'ajout de 2 pour les boucles.

Si vous êtes prêt à renoncer à une petite quantité de vitesse pour un look plus propre, 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

solution la plus rapide que j'ai trouvé (pour une grande liste de toute façon):

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

Fait! Vous pouvez bien sûr aussi activer le dans une liste en exécutant la liste (l)

Cela peut ne pas être le moyen le plus efficace, mais je pensais mettre un one-liner (en fait deux-liner). Les deux versions fonctionnent sur des listes imbriquées de hiérarchie arbitraire, et exploite les caractéristiques linguistiques (Python3.5) et 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 première manière de profondeur. La récursion descend jusqu'à ce qu'il trouve un élément non-liste, puis étend la flist variable locale puis annule au parent. Chaque fois que flist est retourné, il est étendu à la flist du parent dans la compréhension de la liste. Par conséquent, à la racine, une liste plate est retournée.

Ce qui précède une crée plusieurs listes locales et les renvoie qui sont utilisés pour étendre la liste des parents. Je pense que le chemin autour de cela peut être créer un flist gloabl, 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 suis pas sûr à ce moment sur l'efficacité.

Remarque : applique ci-dessous pour Python 3.3+ car il utilise yield_from . six est également un paquet tiers, même si elle est stable. Alternativement, vous pouvez utiliser sys.version.


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

Cependant, considérer ce cas un peu 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ératives, de sorte que les routes ci-dessus échouera ici.
  • élément un, 'abc', est techniquement itérables (tous strs sont). Cependant, la lecture entre les lignes un peu, vous ne voulez pas le traiter comme tel -. Vous voulez le traiter comme un seul élément
  • Le dernier élément, [8, [9, 10]] lui-même est un itérables imbriqué. la compréhension de la liste de base et chain.from_iterable extraire seulement « 1 niveau vers le bas. »

Vous pouvez remédier à cela 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érables avec Iterable , un ABC de itertools, mais veulent aussi faire en sorte que (2) l'élément est pas "string-like."

Une autre approche inhabituelle qui fonctionne pour hétéro- et listes homogènes d'entiers:

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

Une méthode récursive simple à l'aide de reduce 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]

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

Recursion, contrairement à des méthodes telles que les boucles de for et monades, est qu'il est une solution générale non limitée par la profondeur de la liste . Par exemple, une liste avec une profondeur de 5 peut être aplati 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]
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top