Pourquoi Python n’a-t-il pas une fonction «aplatie» pour les listes?


39

Erlang et Ruby sont tous deux dotés de fonctions permettant d'aplatir les tableaux. Cela semble être un outil simple et utile à ajouter à une langue. On pourrait faire ceci:

>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> mess.flatten()
[1, 2, 3, 4, 5, 6]

Ou même:

>>> import itertools
>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> list(itertools.flatten(mess))
[1, 2, 3, 4, 5, 6]

Au lieu de cela, en Python, il faut passer par le problème d'écrire une fonction pour aplatir les tableaux. Cela me semble ridicule, l’aplanissement des tableaux est une chose si courante à faire. C'est comme avoir à écrire une fonction personnalisée pour concaténer deux tableaux.

J'ai googlé cela sans résultat, alors je demande ici; Y a-t-il une raison particulière pour laquelle un langage mature comme Python 3, qui contient cent mille piles différentes, ne fournit pas une méthode simple pour aplatir les tableaux? L'idée d'inclure une telle fonction a-t-elle été discutée et rejetée à un moment donné?


2
@detly: Il m'est arrivé de manquer d'aplatissement récemment lorsque j'utilisais plusieurs requêtes pour récupérer des données provenant de différentes sources. Chaque requête renvoie une liste de dictionnaires, donc à la fin, j'ai une liste de listes de dictionnaires à transformer en liste de dictionnaires. J'ai utilisé une boucle + extendmais aplatir aurait été beaucoup plus élégant. Cependant, je suis blessé si ce motif est assez commun pour justifier un aplatissement dans la bibliothèque standard.
Giorgio

4
"Je veux dire, imaginez si vous introduisez dans votre code un bogue qui modifie par inadvertance la structure de vos données. Aplatir fonctionnera toujours, mais produira des résultats totalement erronés.": C'est l'une des raisons pour lesquelles j'aime les langages à saisie statique. ;-)
Giorgio


2
@BryanOakley Voir aussi le commentaire précédent (mais pas pour les listes à plusieurs niveaux, l'aplatissement est généralement courant)
Izkata

3
Il est intégré à Mathemaica et je l’utilise beaucoup.
Per Alexandersson le

Réponses:


34

Les propositions d’ flattenajout d’ une fonction à la bibliothèque standard apparaissent de temps en temps sur les listes de diffusion python-dev et python-ideas . Les développeurs Python répondent généralement avec les points suivants:

  1. Une mise à niveau à un niveau (transformant un itérable d'iterables en un unique itérable) est une expression triviale d'une ligne (x for y in z for x in y)et, dans tous les cas, se trouve déjà dans la bibliothèque standard sous le nom itertools.chain.from_iterable.

  2. Quels sont les cas d'utilisation pour un aplatissement multiniveau à usage général? Sont-ils vraiment assez convaincants pour que la fonction soit ajoutée à la bibliothèque standard?

  3. Comment un utilisateur multiniveau polyvalent déciderait-il à quel moment l'aplatir et le moment de son départ? Vous pourriez penser qu’une règle du type "tout ce qui prend en charge l’interface itérable" fonctionnerait, mais cela entraînerait une boucle infinie flatten('a').

Voir par exemple Raymond Hettinger :

Il a été discuté ad nauseam sur comp.lang.python. Les gens semblent aimer écrire leurs propres versions de flatten plutôt que de trouver des cas d'utilisation légitimes pour lesquels il n'existe pas de solutions triviales.

Un aplatissement général a besoin de savoir comment on peut dire ce qui est atomique et ce qui peut être subdivisé davantage. En outre, il n'est pas évident d'étendre comment l'algorithme devrait être étendu pour couvrir les entrées avec des structures de données en forme d'arborescence avec des données aux nœuds ainsi que les feuilles (pré-ordre, post-commande, parcours en ordre, etc.)


Juste pour être explicite, cela signifie que la fonction à un niveau flattenpeut être définie comme lambda z: [x for y in z for x in y].
Christopher Martin

1
"Un aplatissement polyvalent a besoin de savoir ce qui est atomique et ce qui peut encore être subdivisé.": Cela ressemble à un problème qui peut être résolu en utilisant la POO: chaque objet peut avoir une flattenméthode. L'implémentation de cette méthode doit faire appel de manière récursive flattenà son sous-composant, si l'objet est un composite. Malheureusement, autant que je sache, chaque valeur n'est pas un objet en Python. En Ruby, ça devrait marcher.
Giorgio

1
une aide à plat pour un niveau à plat plutôt que pour une poursuite "pour dans pour dans" est déjà un bon exemple pour l'OMI. facilement lisible
dtc

2
@Giorgio Python se dérobe à de telles méthodes. Les protocoles sont préférés, et je trouve qu'ils sont beaucoup plus faciles à utiliser qu'avec une conception POO car vous n'avez souvent même pas besoin de beaucoup implémenter.
jpmc26

8

Cela vient avec une telle méthode mais ça ne s'appelle pas aplatir. Cela s'appelle " chaîne ". Il retourne un itérateur sur lequel vous auriez alors besoin d'utiliser la fonction list () pour le transformer en liste. Si vous ne souhaitez pas utiliser de *, vous pouvez utiliser la deuxième version "from_iterator". Cela fonctionne de la même manière en Python 3. Il échouera si l'entrée de liste n'est pas une liste de listes.

[[1], [2, 3], [3, 4, 5]] #yes
[1, 2, [5, 6]] #no

Il y avait à une époque une méthode d' aplatissement dans le module compiler.ast, mais elle était déconseillée dans la version 2.6 puis supprimée dans la version 3.0. La récursion de profondeur arbitraire, nécessaire pour les listes imbriquées de manière arbitraire, ne fonctionne pas bien avec la profondeur de récursivité maximale conservatrice de Python. Le raisonnement en faveur de la suppression du compilateur était en grande partie dû au fait que c'était un désordre . Le compilateur a été transformé en ast mais aplati a été laissé derrière.

Une profondeur arbitraire peut être obtenue avec les tableaux de Numpy et l'aplatissement de cette bibliothèque.


La chain.from_iteratorfonction, comme vous le dites, ne peut être utilisé pour aplatir les listes en deux dimensions. Une fonction d'aplatissement actuelle , qui accepte n'importe quel nombre de listes imbriquées et renvoie une liste unidimensionnelle, serait toujours extrêmement utile dans de nombreux cas (du moins à mon avis)
Hubro

2
@Hubro: "dans beaucoup de cas" - pouvez-vous en nommer six?
Gareth Rees

1
@GarethRees: J'ai donné quelques exemples ici: programmers.stackexchange.com/questions/254279/…
Hubro

J'irais aussi jusqu'à dire que si ces autres langages fournissent effectivement une telle fonctionnalité pour aplatir une liste de la manière très simple décrite, c'est l'un des arguments les plus convaincants en faveur de l'ajout de cette simple capacité à Python.
Bobort

Renvoie-t-il un itérateur ou un générateur?
jpmc26

-1

... peut-être parce que ce n'est pas si difficile d'en écrire un toi-même

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

... et ensuite aplatissez tout ce que vous voulez :)

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]
>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 

8
Le demandeur est conscient de cela: "en Python, il faut passer par le problème d'écrire une fonction pour aplatir les tableaux à partir de zéro". Cela ne tente même pas de répondre à la question posée: "Cela me semble ridicule, il est si courant d’aplanir des tableaux. C’est comme avoir à écrire une fonction personnalisée pour concaténer deux tableaux."
Gnat

1
Hors sujet ... Mais super cool :-) !!
SeF

cette réponse revient à dire à OP qu'il n'est pas un bon développeur car il ne savait pas coder lui-même la fonction. Je vous suggère de modifier le début de votre réponse car il s'agit d'un code utile pour ceux qui trébuchent sur la question, même s'ils sont hors sujet
Federico Bonelli
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.