Comment créer une liste plate à partir d'une liste de listes
-
11-09-2019 - |
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'
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.)
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 remplacerfor sub_x in flatten(x): yield sub_x
- En Python 3.8, les classes de base abstraites déplacé de
collection.abc
au module detyping
.
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):
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 (tousstr
s 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 etchain.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]