Warum hat Python keine "Flatten" -Funktion für Listen?

39

Erlang und Ruby bieten Funktionen zum Reduzieren von Arrays. Es scheint so ein einfaches und nützliches Werkzeug zu sein, um einer Sprache etwas hinzuzufügen. Das könnte man machen:

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

Oder auch:

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

Stattdessen muss man sich in Python die Mühe machen, eine Funktion zu schreiben, mit der Arrays von Grund auf abgeflacht werden. Das kommt mir albern vor, Arrays zu verflachen ist so eine gewöhnliche Sache. Es ist, als müsste man eine benutzerdefinierte Funktion schreiben, um zwei Arrays zu verketten.

Ich habe das fruchtlos gegoogelt, also frage ich hier; Gibt es einen bestimmten Grund, warum eine ausgereifte Sprache wie Python 3, die mit hunderttausenden verschiedenen Batterien geliefert wird, keine einfache Methode zum Reduzieren von Arrays bietet? Wurde die Idee, eine solche Funktion aufzunehmen, diskutiert und irgendwann abgelehnt?

Hubro
quelle
2
@detly: Ich habe in letzter Zeit eine Reduzierung verpasst, als ich mehrere Abfragen zum Abrufen von Daten aus verschiedenen Quellen verwendet habe. Jede Abfrage gibt eine Liste von Wörterbüchern zurück, sodass ich am Ende eine Liste von Wörterbüchern habe, die in eine Liste von Wörterbüchern umgewandelt werden sollen. Ich habe eine Schleife + verwendet, extendaber Abflachen wäre viel eleganter gewesen. Ich wundere mich jedoch, wenn dieses Muster häufig genug ist, um eine Verflachung in der Standardbibliothek zu rechtfertigen.
Giorgio
4
"Ich meine, stellen Sie sich vor, Sie führen einen Fehler in Ihren Code ein, der versehentlich die Struktur Ihrer Daten ändert. Die Reduzierung funktioniert zwar immer noch, führt aber zu völlig falschen Ergebnissen." ;-)
Giorgio
2
@BryanOakley Siehe vorherigen Kommentar auch (obwohl nicht für mehrstufige Listen, Abflachen im Allgemeinen ist üblich)
Izkata
3
Es ist in Mathemaica integriert und ich benutze es ausgiebig.
Per Alexandersson

Antworten:

34

Vorschläge für eine flattenFunktion, die der Standardbibliothek hinzugefügt werden soll, erscheinen von Zeit zu Zeit auf den Mailinglisten von python-dev und python-ideas . Python-Entwickler antworten normalerweise mit den folgenden Punkten:

  1. Eine einstufige Reduzierung (Umwandlung einer Iteration von Iterationen in eine einzige Iteration) ist ein trivialer einzeiliger Ausdruck (x for y in z for x in y)und befindet sich auf jeden Fall bereits in der Standardbibliothek unter dem Namen itertools.chain.from_iterable.

  2. Was sind die Anwendungsfälle für eine allgemeine mehrstufige Abflachung? Sind diese wirklich überzeugend genug, damit die Funktion zur Standardbibliothek hinzugefügt werden kann?

  3. Wie würde eine universelle mehrstufige Abflachung entscheiden, wann sie abgeflacht werden soll und wann sie in Ruhe gelassen werden soll? Sie könnten denken, dass eine Regel wie "Alles reduzieren, was die iterable Schnittstelle unterstützt" funktionieren würde, aber das würde zu einer Endlosschleife für führen flatten('a').

Siehe zum Beispiel Raymond Hettinger :

Es wurde ad nauseam auf comp.lang.python diskutiert . Die Leute scheinen es mehr zu genießen, ihre eigenen Versionen von Flatten zu schreiben, als legitime Anwendungsfälle zu finden, für die es noch keine trivialen Lösungen gibt.

Eine universelle Abflachung muss auf irgendeine Weise erklärt werden, was atomar ist und was weiter unterteilt werden kann. Es ist auch nicht klar, wie der Algorithmus erweitert werden sollte, um Eingaben mit baumartigen Datenstrukturen mit Daten an Knoten sowie den Blättern (Vorbestellung, Nachbestellung, Inorder Traversal usw.) abzudecken.

Gareth Rees
quelle
Um es deutlich zu machen, bedeutet dies, dass die einstufige flattenFunktion definiert werden kann als lambda z: [x for y in z for x in y].
Christopher Martin
1
"Ein universeller Reduzierer muss auf irgendeine Weise erklärt werden, was atomar ist und was weiter unterteilt werden kann.": Dies klingt nach einem Problem, das mit OOP gelöst werden kann: Jedes Objekt könnte eine flattenMethode haben. Die Implementierung dieser Methode sollte flattenihre Unterkomponente rekursiv aufrufen , wenn das Objekt ein zusammengesetztes Objekt ist. Leider ist AFAIK nicht jeder Wert ein Objekt in Python. In Ruby sollte es allerdings funktionieren.
Giorgio
1
Ein Abflachungshelfer für eine einstufige Abflachung und nicht für ein fortgesetztes "In-für-In" ist in diesem Fall IMO bereits gut genug. leicht lesbar
dtc
2
@ Giorgio Python scheut solche Methoden. Protokolle werden bevorzugt, und ich finde, dass die Arbeit mit ihnen weitaus reibungsloser ist als mit einem OOP-Design, da Sie häufig nicht einmal viel implementieren müssen.
jpmc26
8

Es kommt mit einer solchen Methode, aber es nennt es nicht platt. Es heißt " Kette ". Es gibt einen Iterator zurück, den Sie dann mit der Funktion list () wieder in eine Liste umwandeln müssen. Wenn Sie kein * verwenden möchten, können Sie die zweite "from_iterator" -Version verwenden. In Python 3 funktioniert es genauso . Es schlägt fehl, wenn die Listeneingabe keine Liste von Listen ist.

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

Es gab einmal eine Abflachungsmethode im Modul compiler.ast, die jedoch in 2.6 veraltet war und in 3.0 entfernt wurde. Die willkürliche Tiefenrekursion, die für beliebig verschachtelte Listen erforderlich ist, funktioniert mit der konservativen maximalen Rekursionstiefe von Python nicht gut. Die Gründe für das Entfernen des Compilers waren größtenteils darauf zurückzuführen, dass es sich um ein Chaos handelte . Der Compiler wurde in ast verwandelt, aber die Abflachung blieb zurück.

Beliebige Tiefe kann mit numpy's Arrays und der Abflachung dieser Bibliothek erreicht werden.

Weltingenieur
quelle
Die chain.from_iteratorFunktion kann, wie Sie sagten, nur zum Reduzieren zweidimensionaler Listen verwendet werden. Eine tatsächliche Abflachungsfunktion, die beliebige Mengen verschachtelter Listen akzeptiert und eine eindimensionale Liste zurückgibt, wäre in vielen Fällen (zumindest meiner Meinung nach) immer noch
äußerst
2
@ Hubro: "in vielen Fällen" - können Sie sechs nennen?
Gareth Rees
1
@ GarethRees: Ich gab hier ein paar Beispiele: programmers.stackexchange.com/questions/254279/…
Hubro
Ich würde auch so weit gehen zu argumentieren, dass, wenn diese andere Sprache tatsächlich eine solche Funktion bietet, um eine Liste auf die sehr einfache beschriebene Weise zu verflachen, dies eines der überzeugendsten Argumente für das Hinzufügen dieser einfachen Fähigkeit zu Python ist.
Bobort
Gibt es einen Iterator oder einen Generator zurück?
jpmc26
-1

... vielleicht, weil es gar nicht so schwer ist, selbst einen zu schreiben

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

... und dann mach alles flach was du willst :)

>>> 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]
>>> 
Shreyas
quelle
8
asker ist sich dessen bewusst: "In Python muss man sich die Mühe machen, eine Funktion zu schreiben, mit der Arrays von Grund auf abgeflacht werden". Dies versucht nicht einmal, die gestellte Frage zu beantworten: "Mir kommt das albern vor, Arrays zu reduzieren ist so eine häufige Aufgabe. Es ist, als müsste man eine benutzerdefinierte Funktion schreiben, um zwei Arrays zu verketten."
Mücke
1
Aus dem Thema ... Aber super cool :-) !!
SeF
Diese Antwort ist, als würde man OP sagen, dass er kein guter Entwickler ist, weil er nicht wusste, wie er die Funktion selbst codieren kann. Ich schlage vor, dass Sie den Anfang Ihrer Antwort ändern, da dies ein nützlicher Code für diejenigen ist, die über die Frage stolpern, auch wenn sie nicht zum Thema gehören
Federico Bonelli,