Listenverständnis vs Karte

Hallo Habr. Oft stellt sich bei der Arbeit mit Sequenzen die Frage nach deren Entstehung. Es scheint gewohnt zu sein, List Inclusion (List Comprehension) zu verwenden , und in Büchern wird auf die obligatorische Verwendung der integrierten Kartenfunktion hingewiesen.

In diesem Artikel werden diese Ansätze zum Arbeiten mit Sequenzen betrachtet, die Leistung verglichen und auch ermittelt, in welchen Situationen welcher Ansatz besser ist.

Bild

Listenverständnis


Die Aufnahme von Listen ist ein in Python integrierter Mechanismus zur Listenerstellung. Er hat nur eine Aufgabe - eine Liste zu erstellen. Listeneinschluss erstellt eine Liste aus einem beliebigen iterierbaren Typ, wobei die eingehenden Werte transformiert (gefiltert) werden.

Ein Beispiel für einen einfachen Listeneinschluss zum Generieren einer Liste von Quadraten mit Zahlen von 0 bis 9:

squares = [x*x for x in range(10)] 

Ergebnis:

 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

Karte


map ist eine Funktion, die in die Sprache eingebaut ist. Es akzeptiert eine Funktion als ersten Parameter und ein iterierbares Objekt als zweiten. Gibt einen Generator (Python 3.x) oder eine Liste (Python 2.x) zurück. Ich werde Python 3 wählen.

Ein Beispiel für den Aufruf der Kartenfunktion zum Generieren einer Liste von Quadraten mit Zahlen von 0 bis 9:

 squares = list(map(lambda x: x*x, range(10))) 

Ergebnis:

 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

Leistungsvergleich


Bauen ohne Funktionen


Als Experiment betrachten wir die Quadrate von Zahlen aus dem Intervall von 0 bis 9.999.999:

 python -m timeit -r 10 "[x*x for x in range(10000000)]" python -m timeit -r 10 "list(map(lambda x: x*x, range(10000000)))" 

Ergebnisse:

 1 loop, best of 10: 833 msec per loop 1 loop, best of 10: 1.22 sec per loop 

Wie Sie sehen, arbeitet die Listenerfassungsmethode etwa 32% schneller. Demontiert ist es nicht möglich, vollständige Antworten zu erhalten, da die Kartenfunktion „die Details ihrer Arbeit zu verbergen scheint“. Dies liegt aber höchstwahrscheinlich am ständigen Aufruf der Lambda-Funktion, innerhalb derer bereits quadratische Berechnungen durchgeführt werden. Im Falle von List Comprehension müssen wir nur das Quadrat berechnen.

Bauen mit Features


Zum Vergleich betrachten wir auch die Quadrate der Zahlen, aber die Berechnungen befinden sich nun in der Funktion:

 python -m timeit -r 10 -s "def pow2(x): return x*x" "[pow2(x) for x in range(10000000)]" python -m timeit -r 10 -s "def pow2(x): return x*x" "list(map(pow2, range(10000000)))" 

Ergebnisse:

 1 loop, best of 10: 1.41 sec per loop 1 loop, best of 10: 1.21 sec per loop 

Diesmal ist die Situation umgekehrt. Die Kartenmethode war 14% schneller. In diesem Beispiel befinden sich beide Methoden in derselben Situation. Beide müssen eine Funktion aufrufen, um das Quadrat zu berechnen. Die internen Optimierungen der Kartenfunktion ermöglichen es jedoch, bessere Ergebnisse anzuzeigen.

Was soll man wählen?


Nachfolgend finden Sie die Regel zur Auswahl der richtigen Methode:

Bild

Es kann Ausnahmen von dieser Regel geben, aber in den meisten Fällen hilft es Ihnen, die richtige Wahl zu treffen!

ist map "sicherer"?


Warum fordern viele die Verwendung von Karten . Tatsache ist, dass die Karte in einigen Fällen tatsächlich sicherer ist als das Listenverständnis.

Zum Beispiel:

 symbols = ['a', 'b', 'c'] values = [1, 2, 3] for x in symbols: print(x) squared = [x*x for x in values] #  . "x"   "for"  print(x) 

Die Ausgabe des Programms sieht wie folgt aus:

 a 3 b 3 c 3 

Schreiben Sie nun den gleichen Code mit map :

 symbols = ['a', 'b', 'c'] values = [1, 2, 3] for x in symbols: print(x) squared = map(lambda x: x*x, values) #  ,     print(x) 

Fazit:

 a a b b c c 

Die meisten Beobachter konnten bereits anhand der Syntax von map feststellen, dass dies Python 2 ist. Tatsächlich gab es im zweiten Python ein ähnliches Problem beim Überschreiben von Variablen. In Python 3 wurde dieses Problem jedoch behoben und ist nicht mehr relevant.

Die oben beschriebenen Beispiele zeigen die gleichen Ergebnisse. Es mag auch den Anschein haben, dass dies ein dummer Fehler ist, und Sie werden niemals einen solchen Fehler machen. Dies kann jedoch passieren, wenn Sie einfach einen Codeblock mit einer inneren Schleife von einem anderen Block übertragen. Solch ein Fehler kann viel Zeit und Nerven kosten, um ihn zu beheben.

Fazit


Ein Vergleich ergab, dass jede der Methoden in ihrer Situation gut ist.

  • Wenn Sie nicht alle berechneten Werte auf einmal benötigen (oder sie überhaupt nicht benötigen), sollten Sie sich für die Karte entscheiden . Daher fordern Sie nach Bedarf einen Teil der Daten vom Generator an, während Sie viel Speicherplatz sparen (Python 3. In Python 2 ist dies nicht sinnvoll, da map eine Liste zurückgibt).
  • Wenn Sie alle Werte auf einmal berechnen müssen und die Berechnungen ohne Verwendung von Funktionen durchgeführt werden können, sollten Sie eine Auswahl in Richtung List Comprehension treffen. Wie die Ergebnisse von Experimenten zeigen, hat es einen signifikanten Leistungsvorteil.

PS: Wenn ich etwas verpasst habe, diskutiere ich es gerne mit Ihnen in den Kommentaren.

Source: https://habr.com/ru/post/de479252/


All Articles