2009-05-17 3 views
10

Durchläuft map() die Liste wie "for" würde? Gibt es einen Wert bei der Verwendung von map vs for?Gibt es einen Wert bei der Verwendung von map() vs für?

Wenn ja, jetzt mein Code sieht wie folgt aus:

for item in items: 
    item.my_func() 

Wenn es Sinn macht, möchte ich es (machen Karte). Ist das möglich? Wie ist ein Beispiel?

Antwort

23

Sie map anstelle der for Schleife verwenden, könnten Sie gezeigt haben, aber da Sie nicht das Ergebnis von item.my_func() zu verwenden scheinen, ist dies nicht empfohlen. map sollte verwendet werden, wenn Sie eine Funktion ohne Nebenwirkungen auf alle Elemente einer Liste anwenden möchten. In allen anderen Situationen verwenden Sie eine explizite for-Schleife.

Auch, wie von Python 3.0 map gibt einen Generator, so dass in diesem Fall map nicht gleich verhalten (es sei denn, Sie explizit alle Elemente auszuwerten durch den Generator zurückgeführt, zum Beispiel durch list darauf Aufruf).


bearbeiten: kibibu fragt in den Kommentaren für eine Klarstellung darüber, warum map s erste Argument sollte nicht eine Funktion mit Nebenwirkungen. Ich antworte auf diese Frage eine Aufnahme:

map soll eine Funktion fin the mathematical sense übergeben werden. Unter diesen Umständen spielt es keine Rolle, in welcher Reihenfolge f auf die Elemente des zweiten Arguments angewendet wird (vorausgesetzt, dass sie natürlich in ihrer ursprünglichen Reihenfolge zurückgegeben werden). unter diesen Umständen noch wichtiger ist, ist map(g, map(f, l)) semantisch äquivalent map(lambda x: g(f(x)), l), unabhängig von der Reihenfolge, in der f und g an ihren jeweiligen Eingängen angewendet werden.

Zum Beispiel ist es egal, ob map zurückgibt und Iterator oder eine vollständige Liste auf einmal. Wenn jedoch f und/oder g Nebenwirkungen verursachen, dann ist diese Äquivalenz nur dann, wenn die Semantik der map(g, map(f, l)) solche garantiert sind, dass in jeder Phase g an die ersten n Elemente durch map(f, l) vor map(f, l) gilt f an die zurück angewendet wird (n + 1) ​ st Element von l. (Was bedeutet, dass map müssen die faulste möglich Iteration durchführen ---, die es funktioniert in Python 3, aber nicht in Python 2!)

Einen Schritt weiter gehen: Auch wenn wir die Python 3 Implementierung von map, die semantische Äquivalenz übernehmen kann leicht zusammenbrechen, wenn der Ausgang von map(f, l) ist z passierte durch itertools.tee, bevor es an den äußeren map Anruf geliefert wurde.

Die obige Diskussion mag theoretischer Natur erscheinen, aber wenn Programme komplexer werden, werden sie schwieriger zu diskutieren und daher schwieriger zu debuggen. Sicherzustellen, dass einige Dinge invariant sind, mildert dieses Problem etwas und kann tatsächlich eine ganze Klasse von Fehlern verhindern.

Schließlich erinnert map viele Menschen an ihr wirklich funktionales Gegenstück in verschiedenen (rein) funktionalen Sprachen. Wenn man eine "Funktion" mit Nebenwirkungen passiert, werden diese Leute verwirrt. Wenn daher die Alternative (dh die Verwendung einer expliziten Schleife) nicht schwieriger zu implementieren ist als ein Aufruf an map, wird dringend empfohlen, die Verwendung von map auf Fälle zu beschränken, in denen die anzuwendende Funktion keine Nebenwirkungen verursacht .

+0

Danke allen! – roder

+0

Warum ist es nicht empfehlenswert? Ist es ein Semantik-Ding oder eine Art verteiltes Verarbeitungs-Ding (wo Nebenwirkungen Parallelität brechen)? – kibibu

+1

@ kibibu: Ich habe angefangen, Ihre Frage zu beantworten, aber endete mit viel zu viel Text für dieses Kommentarfeld zu enthalten. Also habe ich die Antwort aktualisiert :) Wie das hilft! (Was Ihre Bemerkung über die verteilte Verarbeitung des Iterablen angeht: Obwohl das * als zusätzliches Argument angeführt werden könnte, wenn in anderen Sprachen von 'map' gesprochen wird, glaube ich nicht, dass Python (derzeit) irgendwelche Optimierungen dieser Art durchführt.) – Stephan202

-3
map(lambda item: item.my_func(), items) 
+1

Dies funktioniert, aber beantwortet nicht die Frage, warum dies besser ist als die Verwendung einer for-Schleife (nämlich ist es nicht). – Kiv

1

Der Hauptvorteil von map ist, wenn Sie das Ergebnis einer Berechnung für jedes Element in einer Liste erhalten möchten. Zum Beispiel verdoppelt sich diese Schnipsel jeden Wert in einer Liste:

map(lambda x: x * 2, [1,2,3,4]) #=> [2, 4, 6, 8] 

Es ist wichtig zu beachten, dass map eine neue Liste mit den Ergebnissen zurück. Die ursprüngliche Liste wird nicht geändert.

Um das Gleiche mit for zu tun, müssten Sie eine leere Liste erstellen und eine zusätzliche Zeile zum for Körper hinzufügen, um das Ergebnis jeder Berechnung der neuen Liste hinzuzufügen. Die map Version ist prägnanter und funktionaler.

+4

Listenübersetzungen sind jedoch noch prägnanter, z. [x * 2 für x in [1,2,3,4]) in Ihrem Beispiel. – Kiv

5

Sie können diese mit der Karte wie folgt schreiben:

map(cls.my_func, items) 

cls mit der Klasse der Elemente ersetzen Sie über iterieren.

Wie von Stephan202 erwähnt, ist dies in diesem Fall nicht empfohlen.

Wenn Sie eine neue Liste erstellen möchten, indem Sie für jedes Element in der Liste eine Funktion anwenden, verwenden Sie in der Regel map. Dies hat die implizierte Bedeutung, dass die Funktion keine Nebenwirkung hat und Sie die Karte (möglicherweise) parallel ausführen könnten.

Wenn Sie keine neue Liste erstellen möchten oder die Funktion Nebenwirkungen hat, verwenden Sie eine for-Schleife. Dies ist in Ihrem Beispiel der Fall.

2

Es gibt einen leichten semantischen Unterschied, der wahrscheinlich in der Python-Sprachspezifikation geschlossen ist. Die Karte ist explizit parallelisierbar, während für nur in speziellen Situationen. Code kann brechen aus für, aber nur entkommen mit Ausnahme von Karte.

Meiner Meinung nach Karte sollte für muss während nicht auch der Reihenfolge der Funktionsanwendung garantieren. AFAIK no Python-Implementierung ist derzeit in der Lage, diese automatische Parallelisierung durchzuführen.

2

Sie können Ihre map zu einem coolen OR-oder Multiprozessor-OR verteilte Computing-Framework wechseln, wenn Sie müssen. Disco ist ein Beispiel für verteilte, resistent gegen Fehler Erlang-Python-basierte Framework. Ich habe es auf 2 Boxen mit 8 Kernen konfiguriert und jetzt läuft mein Programm dank des Disco-Clusters 16-mal schneller, allerdings musste ich mein Programm aus Listen-Comprehensions und for-Loops in map/reduce umschreiben.

Es ist der gleiche Deal, um ein Programm mit for-Schleifen und Listenkomprehensionen und map/reduce zu schreiben, aber wenn Sie es auf einem Cluster ausführen müssen, können Sie es fast kostenlos tun, wenn Sie map/reduce verwendet haben.Wenn du es nicht getan hast, wirst du es umschreiben müssen.

Vorsicht: Soweit ich weiß, gibt Python 2.x eine Liste anstelle eines Iterators aus der Karte zurück. Ich habe gehört, dass dies umgangen werden kann, indem man iter.imap() verwendet (aber nie benutzt).

2

Verwenden Sie eine explizite for-Schleife, wenn Sie keine Ergebnisliste benötigen (z. B. Funktionen mit Nebeneffekten).

Verwenden Sie ein Listenverständnis, wenn Sie eine Ergebnisliste benötigen (z. B. Funktionen, die direkt auf der Eingabe einen Wert zurückgeben).

Verwenden Sie map(), wenn Sie versuchen, Lisp-Benutzer davon zu überzeugen, dass es sich lohnt, Python zu verwenden. ;)

0

Die Karte kann manchmal für eingebaute Funktionen schneller sein als die manuelle Codierung einer for-Schleife. Versuchen Sie Timing-Karte (str, Bereich (1000000)) vs. eine ähnliche for-Schleife.

Verwandte Themen