Oui, je sais que ce sujet a déjà été abordé ( ici , ici , ici , ici ), mais pour autant que je sache, toutes les solutions, sauf une, échouent sur une liste comme celle-ci:
L = [[[1, 2, 3], [4, 5]], 6]
Où la sortie souhaitée est
[1, 2, 3, 4, 5, 6]
Ou peut-être encore mieux, un itérateur. La seule solution que j'ai vue qui fonctionne pour une imbrication arbitraire se trouve dans cette question :
def flatten(x):
result = []
for el in x:
if hasattr(el, "__iter__") and not isinstance(el, basestring):
result.extend(flatten(el))
else:
result.append(el)
return result
flatten(L)
Est-ce le meilleur modèle? Ai-je oublié quelque chose? Des problèmes?
python
list
optimization
nested-lists
flatten
telliott99
la source
la source
list
s destinés à être homogène) ne signifie pas que c'est la faute d'un Python et nous avons besoin d' une commande intégrée pour une telle tâcheRéponses:
L'utilisation des fonctions du générateur peut rendre votre exemple un peu plus facile à lire et probablement augmenter les performances.
Python 2
J'ai utilisé l' ABC Iterable ajouté en 2.6.
Python 3
En Python 3, le
basestring
n'est plus, mais vous pouvez utiliser un tuple destr
etbytes
pour y obtenir le même effet.L'
yield from
opérateur renvoie un élément d'un générateur un à la fois. Cette syntaxe de délégation à un sous-générateur a été ajoutée en 3.3la source
l = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))
en un clin d'œil quand je l'ai faitlist(flatten(l))
. Tous les autres commenceraient à travailler et prendraient une éternité!collections.Sequence
place decollections.Iteratable
?for i in flatten(42): print (i)
. Cela pourrait être corrigé en déplaçant leisinstance
-test et la clause else en dehors de lafor el
boucle. (Ensuite, vous pourriez y jeter n'importe quoi, et cela en ferait une liste aplatie)collections.Iterable
est déconseillée. Utilisezcollections.abc.Iterable
plutôt.Ma solution:
Un peu plus concis, mais à peu près la même chose.
la source
try: iter(x)
tester s'il est itérable… Mais je ne pense pas que devoir importer un module stdlib soit un inconvénient à éviter.int
def flatten(x): return [a for i in x for a in flatten(i)] if isinstance(x, collections.Iterable) else [x]
- mais la lisibilité pourrait être subjective ici.if isinstance(x, collections.Iterable) and not isinstance(x, basestring)
collections.Iterable
parlist
Générateur utilisant la récursion et le typage canard (mis à jour pour Python 3):
la source
for i in flatten(item): yield i
Version génératrice de la solution non récursive de @ unutbu, comme l'a demandé @Andrew dans un commentaire:
Version légèrement simplifiée de ce générateur:
la source
Voici ma version fonctionnelle d'aplatir récursif qui gère à la fois les tuples et les listes, et vous permet d'ajouter n'importe quel mélange d'arguments positionnels. Renvoie un générateur qui produit la séquence entière dans l'ordre, arg par arg:
Usage:
la source
e
,a
,n
reportez - vous àargs
pourn
,intermediate
(ou le plus courtmid
ou vous préférez peut-êtreelement
) poura
etresult
poure
, donc:flatten = lambda *args: (result for mid in args for result in (flatten(*mid) if isinstance(mid, (tuple, list)) else (mid,)))
compiler.ast.flatten
. Grand code compact, fonctionne pour tout type d'objet (je pense).Cette version
flatten
évite la limite de récursivité de python (et fonctionne donc avec des itérables imbriqués profondément arbitraires). C'est un générateur qui peut gérer des chaînes et des itérables arbitraires (même infinis).Voici quelques exemples démontrant son utilisation:
Bien qu'il
flatten
puisse gérer des générateurs infinis, il ne peut pas gérer l'imbrication infinie:la source
sets
,dicts
,deques
,listiterators
,generators
, Et les classes descripteurs de fichiers personnalisés avec__iter__
définis sont toutes les instances decollections.Iterable
, mais pascollections.Sequence
. Le résultat de l'aplatissement d'undict
est un peu incertain, mais sinon, je pense quecollections.Iterable
c'est un meilleur défaut quecollections.Sequence
. C'est certainement le plus libéral.collections.Iterable
est que cela inclut des générateurs infinis. J'ai changé ma réponse pour gérer ce cas.StopIteration
. En outre, ilwhile True: first = next(remainder)
pourrait être remplacé parfor first in remainder:
.try-except StopIteration block
.Voici une autre réponse encore plus intéressante ...
Fondamentalement, il convertit la liste imbriquée en chaîne, utilise une expression régulière pour supprimer la syntaxe imbriquée, puis reconvertit le résultat en une liste (aplatie).
la source
[['C=64', 'APPLE ]['], ['Amiga', 'Mac', 'ST']]
:) D'un autre côté, étant donné une liste qui se contient, cela fera un peu mieux que les autres réponses, soulevant un exception au lieu de simplement boucler jusqu'à épuisement de la mémoire / récurrence jusqu'à épuisement de la pile…[x for x in c]
est-ce juste une façon lente et verbeuse d'en faire une copiec
, alors pourquoi feriez-vous cela? Deuxièmement, votre code va clairement se convertir'APPLE ]['
en'APPLE '
, car il ne gère pas les guillemets, il suppose simplement que tous les crochets sont des crochets de liste.arr_str = str(arr)
et puis[int(s) for s in re.findall(r'\d+', arr_str)]
vraiment. Voir github.com/jorgeorpinel/flatten_nested_lists/blob/master/…la source
Vous pouvez utiliser à
deepflatten
partir du package tiersiteration_utilities
:Il s'agit d'un itérateur, vous devez donc l'itérer (par exemple en l'enveloppant
list
ou en l'utilisant en boucle). En interne, il utilise une approche itérative au lieu d'une approche récursive et il est écrit en extension C afin qu'il puisse être plus rapide que les approches python pures:Je suis l'auteur de la
iteration_utilities
bibliothèque.la source
C'était amusant d'essayer de créer une fonction qui pourrait aplatir une liste irrégulière en Python, mais bien sûr, c'est à cela que sert Python (pour rendre la programmation amusante). Le générateur suivant fonctionne assez bien avec certaines mises en garde:
Il va aplatir les types de données que vous voudrez peut - être laissé seul (comme
bytearray
,bytes
etstr
objets). De plus, le code repose sur le fait que demander un itérateur à un non-itérable soulève aTypeError
.Éditer:
Je ne suis pas d'accord avec la mise en œuvre précédente. Le problème est que vous ne devriez pas pouvoir aplatir quelque chose qui n'est pas itérable. C'est déroutant et donne une mauvaise impression de l'argument.
Le générateur suivant est presque le même que le premier mais n'a pas le problème d'essayer d'aplatir un objet non itérable. Il échoue comme on pourrait s'y attendre lorsqu'un argument inapproprié lui est donné.
Le test du générateur fonctionne correctement avec la liste fournie. Cependant, le nouveau code lèvera un
TypeError
quand un objet non itérable lui sera donné. Des exemples sont présentés ci-dessous du nouveau comportement.la source
Bien qu'une réponse élégante et très pythonique ait été choisie, je présenterais ma solution juste pour la revue:
Veuillez dire à quel point ce code est bon ou mauvais?
la source
isinstance(i, (tuple, list))
. L'initialisation de variables vides est un indicateur pour moi de chercher des structures de code alternatives, généralement des compréhensions, des générateurs, une récursivité, etc.return type(l)(ret)
vous récupérera également le même type de conteneur que celui transmis. :)Je préfère des réponses simples. Pas de générateurs. Aucune récursivité ou limite de récursivité. Juste itération:
Cela fonctionne avec deux listes: une boucle for interne et une boucle while externe.
La boucle for interne parcourt la liste. S'il trouve un élément de liste, il (1) utilise list.extend () pour aplatir ce niveau d'imbrication de première partie et (2) les commutateurs keepChecking à True. keepchecking est utilisé pour contrôler la boucle while externe. Si la boucle externe est définie sur true, elle déclenche la boucle interne pour un autre passage.
Ces passes continuent de se produire jusqu'à ce qu'aucune liste imbriquée ne soit trouvée. Lorsqu'un passage se produit finalement là où aucun n'est trouvé, keepChecking n'est jamais déclenché sur true, ce qui signifie que listIsNested reste faux et que la boucle while externe se ferme.
La liste aplatie est ensuite renvoyée.
Essai
[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]
la source
Voici une fonction simple qui aplatit les listes de profondeur arbitraire. Aucune récursivité, pour éviter un débordement de pile.
la source
Je suis surpris que personne n'y ait pensé. Maudite récursivité Je n'ai pas les réponses récursives que les gens avancés ici ont faites. de toute façon voici ma tentative à ce sujet. la mise en garde est qu'il est très spécifique au cas d'utilisation de l'OP
production:
la source
Je ne suis pas passé par toutes les réponses déjà disponibles ici, mais voici une ligne unique que j'ai trouvée, empruntant à la manière de lisp de traiter en premier et en liste de repos
voici un cas simple et un cas pas si simple -
la source
def foo():
c'est une ligne distincte. C'est également très illisible.Lorsque vous essayez de répondre à une telle question, vous devez vraiment donner les limites du code que vous proposez comme solution. S'il ne s'agissait que de performances, cela ne me dérangerait pas trop, mais la plupart des codes proposés comme solution (y compris la réponse acceptée) ne parviennent pas à aplatir une liste dont la profondeur est supérieure à 1000.
Quand je dis la plupart des codes, je veux dire tous les codes qui utilisent n'importe quelle forme de récursivité (ou appellent une fonction de bibliothèque standard qui est récursive). Tous ces codes échouent car pour chaque appel récursif effectué, la pile (d'appel) augmente d'une unité et la pile d'appel python (par défaut) a une taille de 1000.
Si vous n'êtes pas trop familier avec la pile d'appels, alors peut-être que ce qui suit vous aidera (sinon vous pouvez simplement faire défiler jusqu'à la mise en œuvre ).
Taille de la pile d'appels et programmation récursive (analogie avec les donjons)
Trouver le trésor et sortir
Imaginez que vous entrez dans un immense donjon avec des chambres numérotées , à la recherche d'un trésor. Vous ne connaissez pas l'endroit mais vous avez des indications sur la façon de trouver le trésor. Chaque indication est une énigme (la difficulté varie, mais vous ne pouvez pas prédire à quel point elles seront difficiles). Vous décidez de réfléchir un peu à une stratégie pour gagner du temps, vous faites deux constats:
En entrant dans le donjon, vous remarquez un petit cahier ici. Vous décidez de l'utiliser pour noter chaque pièce que vous quittez après avoir résolu une énigme (lorsque vous entrez dans une nouvelle pièce), de cette façon, vous pourrez retourner à l'entrée. C'est une idée géniale, vous ne dépenserez même pas un centime pour mettre en œuvre votre stratégie.
Vous entrez dans le donjon, résolvant avec grand succès les 1001 premières énigmes, mais voici quelque chose que vous n'aviez pas planifié, vous n'avez plus de place dans le cahier que vous avez emprunté. Vous décidez d' abandonner votre quête car vous préférez ne pas avoir le trésor que d'être perdu pour toujours à l'intérieur du donjon (cela a l'air intelligent en effet).
Exécuter un programme récursif
Fondamentalement, c'est exactement la même chose que de trouver le trésor. Le donjon est la mémoire de l' ordinateur , votre objectif n'est plus de trouver un trésor mais de calculer une fonction (trouver f (x) pour un x donné ). Les indications sont simplement des sous-routines qui vous aideront à résoudre f (x) . Votre stratégie est la même que la stratégie de pile d'appels , le bloc-notes est la pile, les salles sont les adresses de retour des fonctions:
Le problème que vous avez rencontré dans le donjon sera le même ici, la pile d'appels a une taille finie (ici 1000) et donc, si vous entrez trop de fonctions sans revenir en arrière, vous remplirez la pile d'appels et aurez une erreur qui ressemblera comme
« aventurier Cher, je suis désolé , mais votre ordinateur portable est pleine »:RecursionError: maximum recursion depth exceeded
. Notez que vous n'avez pas besoin de récursivité pour remplir la pile d'appels, mais il est très peu probable qu'un programme non récursif appelle 1000 fonctions sans jamais revenir. Il est important de comprendre également qu'une fois que vous êtes revenu d'une fonction, la pile d'appels est libérée de l'adresse utilisée (d'où le nom "pile", l'adresse de retour est poussée avant d'entrer dans une fonction et retirée lors du retour). Dans le cas particulier d'une récursion simple (une fonctionf
qui s'appelle lui-même encore et encore -) vous entrerezf
encore et encore jusqu'à ce que le calcul soit terminé (jusqu'à ce que le trésor soit trouvé) et reviendrezf
jusqu'à ce que vous retourniez à l'endroit où vous avez appeléf
en premier lieu. La pile d'appels ne sera jamais libérée de quoi que ce soit jusqu'à la fin où elle sera libérée de toutes les adresses de retour l'une après l'autre.Comment éviter ce problème?
C'est en fait assez simple: "n'utilisez pas la récursivité si vous ne savez pas jusqu'où elle peut aller". Ce n'est pas toujours vrai car dans certains cas, la récursivité des appels de queue peut être optimisée (TCO) . Mais en python, ce n'est pas le cas, et même une fonction récursive "bien écrite" n'optimisera pas l' utilisation de la pile. Il y a un article intéressant de Guido sur cette question: Tail Recursion Elimination .
Il existe une technique que vous pouvez utiliser pour rendre toute fonction récursive itérative, cette technique que nous pourrions appeler apporter votre propre cahier . Par exemple, dans notre cas particulier, nous explorons simplement une liste, entrer dans une pièce équivaut à entrer une sous-liste, la question que vous devez vous poser est de savoir comment puis-je revenir d'une liste à sa liste parente? La réponse n'est pas si complexe, répétez ce qui suit jusqu'à ce que le
stack
soit vide:address
etindex
dans unstack
lors de la saisie d'une nouvelle sous-liste (notez qu'une adresse de liste + index est également une adresse, nous utilisons donc exactement la même technique utilisée par la pile des appels);yield
il (ou les ajouter dans une liste);stack
retouraddress
(etindex
) .Notez également que cela équivaut à un DFS dans une arborescence où certains nœuds sont des sous-listes
A = [1, 2]
et certains sont des éléments simples:0, 1, 2, 3, 4
(pourL = [0, [1,2], 3, 4]
). L'arbre ressemble à ceci:La pré-commande de parcours DFS est: L, 0, A, 1, 2, 3, 4. N'oubliez pas que pour implémenter un DFS itératif, vous avez également besoin d'une pile. L'implémentation que j'ai proposée auparavant aboutit à avoir les états suivants (pour le
stack
et leflat_list
):Dans cet exemple, la taille maximale de la pile est 2, car la liste d'entrée (et donc l'arborescence) a la profondeur 2.
la mise en oeuvre
Pour l'implémentation, en python, vous pouvez simplifier un peu en utilisant des itérateurs au lieu de simples listes. Les références aux (sous) itérateurs seront utilisées pour stocker les adresses de retour des sous-listes (au lieu d'avoir à la fois l'adresse de liste et l'index). Ce n'est pas une grande différence mais je pense que c'est plus lisible (et aussi un peu plus rapide):
Notez également que dans
is_list_like
I haveisinstance(item, list)
, qui pourrait être modifié pour gérer plus de types d'entrée, ici, je voulais juste avoir la version la plus simple où (itérable) n'est qu'une liste. Mais vous pouvez aussi le faire:Cela considère les chaînes comme des «éléments simples» et, par conséquent,
flatten_iter([["test", "a"], "b])
sera renvoyé["test", "a", "b"]
et non["t", "e", "s", "t", "a", "b"]
. Remarquez que dans ce cas,iter(item)
est appelé deux fois sur chaque élément, supposons que c'est un exercice pour le lecteur de rendre ce nettoyeur.Tests et remarques sur d'autres implémentations
En fin de compte, n'oubliez pas que vous ne pouvez pas imprimer une liste imbriquée à l'infini en
L
utilisantprint(L)
car en interne, il utilisera des appels récursifs à__repr__
(RecursionError: maximum recursion depth exceeded while getting the repr of an object
). Pour la même raison, les solutions à l'flatten
implicationstr
échoueront avec le même message d'erreur.Si vous devez tester votre solution, vous pouvez utiliser cette fonction pour générer une liste imbriquée simple:
Ce qui donne:
build_deep_list(5)
>>>[4, [3, [2, [1, [0]]]]]
.la source
Voici l'
compiler.ast.flatten
implémentation de 2.7.5:Il existe des méthodes meilleures et plus rapides (si vous avez atteint ici, vous les avez déjà vues)
Notez également:
la source
totalement hacky mais je pense que cela fonctionnerait (selon votre data_type)
la source
Utilisez simplement une
funcy
bibliothèque:pip install funcy
la source
Voici une autre approche py2, je ne sais pas si c'est la plus rapide ou la plus élégante ni la plus sûre ...
Il peut ignorer tout type spécifique (ou dérivé) que vous souhaitez, il renvoie un itérateur, vous pouvez donc le convertir en n'importe quel conteneur spécifique tel que liste, tuple, dict ou simplement le consommer afin de réduire l'empreinte mémoire, pour le meilleur ou pour le pire. il peut gérer les objets non itérables initiaux tels que int ...
Notez que la plupart du gros du travail se fait en C, car pour autant que je sache, c'est comment les outils itertools sont implémentés, donc bien qu'il soit récursif, AFAIK il n'est pas limité par la profondeur de récursivité python puisque les appels de fonction se produisent en C, bien que cela ne signifie pas que vous êtes limité par la mémoire, spécialement sous OS X où sa taille de pile a une limite stricte à partir d'aujourd'hui (OS X Mavericks) ...
il existe une approche légèrement plus rapide, mais une méthode moins portable, ne l'utilisez que si vous pouvez supposer que les éléments de base de l'entrée peuvent être déterminés explicitement sinon, vous obtiendrez une récursion infinie, et OS X avec sa taille de pile limitée, lancer un défaut de segmentation assez rapidement ...
ici, nous utilisons des ensembles pour vérifier le type, il faut donc O (1) vs O (nombre de types) pour vérifier si un élément doit être ignoré ou non, bien que toute valeur avec un type dérivé des types ignorés indiqués échoue , c'est pourquoi son utilisation
str
,unicode
alors utilisez-le avec prudence ...tests:
la source
Sans utiliser de bibliothèque:
la source
En utilisant
itertools.chain
:Ou sans enchaîner:
la source
J'ai utilisé récursif pour résoudre la liste imbriquée avec n'importe quelle profondeur
Donc, après avoir défini la fonction combine_nlist, il est facile d'utiliser cette fonction pour l'aplatissement. Ou vous pouvez le combiner en une seule fonction. J'aime ma solution car elle peut être appliquée à n'importe quelle liste imbriquée.
résultat
la source
current_value = combiner(current_value,each_item) RecursionError: maximum recursion depth exceeded
La manière la plus simple est d'utiliser la bibliothèque de morphing à l' aide de
pip install morph
.Le code est:
la source
Je suis conscient qu'il existe déjà de nombreuses réponses impressionnantes, mais je voulais ajouter une réponse qui utilise la méthode de programmation fonctionnelle pour résoudre la question. Dans cette réponse, j'utilise la double récursivité:
production:
la source
Je ne sais pas si c'est nécessairement plus rapide ou plus efficace, mais voici ce que je fais:
La
flatten
fonction transforme ici la liste en une chaîne, supprime tous les crochets, rattache les crochets aux extrémités et la transforme en liste.Bien que, si vous saviez que vous auriez des crochets dans votre liste sous forme de chaînes
[[1, 2], "[3, 4] and [5]"]
, vous devriez faire autre chose.la source
Ceci est un simple implémentation d'aplatir sur python2
la source
Cela aplatira une liste ou un dictionnaire (ou une liste de listes ou de dictionnaires de dictionnaires, etc.). Il suppose que les valeurs sont des chaînes et crée une chaîne qui concatène chaque élément avec un argument séparateur. Si vous le souhaitez, vous pouvez utiliser le séparateur pour diviser le résultat en un objet liste par la suite. Il utilise la récursivité si la valeur suivante est une liste ou une chaîne. Utilisez l'argument clé pour indiquer si vous souhaitez les clés ou les valeurs (définir la clé sur faux) de l'objet dictionnaire.
rendements:
la source
Si vous aimez la récursivité, cela pourrait être une solution qui vous intéresse:
J'ai en fait adapté cela à partir d'un code de schéma d'entraînement que j'avais écrit il y a quelque temps.
Prendre plaisir!
la source
Je suis nouveau sur python et viens d'un arrière-plan lisp. Voici ce que j'ai trouvé (consultez les noms var pour lulz):
Semble fonctionner. Tester:
Retour:
la source