2009-12-29 11 views
33

Dies wird in Bezug auf die folgenden: (In Python-Code)Wann sollte ich anstelle einer For-Schleife eine Map verwenden?

for i in object: 
    doSomething(i) 

gegen

map(doSomething, object) 

Beide sind leicht zu verstehen, und kurz, aber gibt es eine Geschwindigkeitsdifferenz? Nun, wenn doSomething einen Rückgabewert hatte, mussten wir überprüfen, ob es als Liste von der Karte zurückgegeben würde, und in der for-Schleife konnten wir entweder unsere eigene Liste erstellen oder einzeln prüfen.

for i in object: 
    returnValue = doSomething(i) 
    doSomethingWithReturnValue(returnValue) 

gegen

returnValue = map(doSomething, object) 
map(doSomethingWithReturnValue, returnValue) 

Jetzt fühle ich die beiden ein wenig auseinander. Die beiden doSomethingWithReturnValue-Funktionen können sich unterscheiden, je nachdem, ob sie während des Durchlaufens der Schleife im laufenden Betrieb überprüft werden oder ob alle Prüfungen gleichzeitig am Ende zu unterschiedlichen Ergebnissen führen. Es scheint auch, dass die for-Schleife immer funktioniert, vielleicht langsamer, wo die Karte nur in bestimmten Szenarien funktionieren würde. Natürlich könnten wir Verrenkungen machen, um beide Arbeiten zu machen, aber das Wichtigste ist, diese Art von Arbeit zu vermeiden.

Was ich suche ist ein Szenario, in dem die Mapping-Funktion wirklich im Vergleich zu einem gut für Loop in Leistung, Lesbarkeit, Wartbarkeit oder Geschwindigkeit der Implementierung. Wenn die Antwort darin besteht, ist wirklich kein großer Unterschied, dann würde ich gerne wissen, wann in der Praxis Leute das eine oder das andere verwenden, oder wenn es wirklich völlig willkürlich ist und durch Codierungsstandards eingestellt wird, die von Ihrer Institution abhängen.

Danke!

+2

Sie können Liste/Wörterbuch/Satz Verständnis oder einen Generator anstelle einer Karte verwenden - hängt davon ab, was die DoSomething() tut. –

+2

nicht versuchen, Leistung vorläufig zu optimieren. Ich würde immer die beste lesbare Option nehmen. Die Laufzeit wird später angezeigt, wenn die showstopper-Leistung ein Problem ist und Sie die Geschwindigkeit oder die Ressourcennutzung verbessern. –

Antwort

22

map ist nützlich, wenn Sie die Funktion auf jedes Element eines Iterablen anwenden und eine Liste der Ergebnisse zurückgeben möchten. Dies ist einfacher und prägnanter als die Verwendung einer for-Schleife und das Erstellen einer Liste.

for ist oft besser lesbar für andere Situationen, und in Lisp gab es viele Iterationskonstrukte, die im Grunde mit Makros und Karte geschrieben wurden. Verwenden Sie in Fällen, in denen map nicht passt, eine for Schleife. Wenn wir einen Compiler/Interpreter hatten, der schlau genug war, um mehrere CPUs/Prozessoren zu verwenden, könnte theoretisch map schneller implementiert werden, da die verschiedenen Operationen auf jedem Element parallel ausgeführt werden könnten. Ich denke jedoch, dass dies derzeit nicht der Fall ist.

+0

PLINQ (C#) kann das aber tun. –

+12

Warum die Vergangenheitsform? Lisp ist am Leben und tritt. – Svante

+3

Eigentlich 'map' schlägt' for' in der Performance sogar in single thread, weil die Schleife in C geschrieben ist. Siehe meinen Beitrag für Geschwindigkeitstest. – iamamac

7

Verwenden Sie einfach Listen Comprehensions: sie sind mehr Python. Sie haben auch eine ähnliche Syntax wie Generator-Ausdrücke, die es einfach macht, von einem zum anderen zu wechseln. Sie müssen nichts ändern, wenn Sie Ihren Code in py3k konvertieren: map gibt in py3k ein iterables zurück und Sie müssen Ihren Code anpassen.

Wenn Sie sich nicht um Rückgabewerte kümmern, benennen Sie nicht die neue Liste, Sie müssen die Rückgabewerte einmal in Ihrem Code verwenden, Sie können zu Generatorausdrücken und einem einzigen Listenverständnis am Ende wechseln.

0

EDIT: Ich erkannte nicht, dass mapitertools.imap nach Python 3.0 entspricht. Daher ist die Schlussfolgerung hier möglicherweise nicht korrekt. Ich werde den Test auf Python 2.6 morgen erneut ausführen und das Ergebnis posten.

Wenn doSomething ist sehr „klein“, map viel schneller als for Schleife sein kann, oder eine Liste Verständnis:

# Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] on win32 

from timeit import timeit 

do = lambda i: i+1 

def _for(): 
    for i in range(1000): 
    do(i) 

def _map(): 
    map(do, range(1000)) 

def _list(): 
    [do(i) for i in range(1000)] 

timeit(_for, number=10000) # 2.5515936921388516 
timeit(_map, number=10000) # 0.010167432629884843 
timeit(_list, number=10000) # 3.090125159839033 

Dies liegt daran, map in C geschrieben ist, während for Schleife und List-Verständnis in Python virtuelle Maschine laufen.

+1

Ich weiß nicht, woher du deine Zahlen bekommst, aber in meinem Fall (Python 2.6) ist die for-Schleife um etwa 5% schneller. Ihr Code ist nicht einmal korrekt, da _list weniger Iterationen ausführt. Die großen Unterschiede, die Sie erhalten haben, deuten darauf hin, dass mit Ihrer Einrichtung etwas nicht stimmt. – interjay

+0

das ist einfach lächerlich. Ihre Codes sind überhaupt nicht gleichwertig. lies meine Antwort. sogar ohne Map Objekt vs. Liste Dichotomie Ihr Code ist anders, es ist eine Schande wirklich, dass Sie es nicht sehen – SilentGhost

+0

Sorry, es ist ein Tippfehler, wenn ich den Code kopiert und formatiert habe. Ich starte Python 3.1.1 auf meinem Core Duo 4300 PC, 'map' schlägt die anderen beiden deutlich. – iamamac

10

Kennen Sie das timeit Modul? Im Folgenden sind einige Timings. -s führt eine einmalige Konfiguration durch, und dann wird der Befehl wiederholt und die beste Zeit aufgezeichnet.

1> python -m timeit -s "L=[]; M=range(1000)" "for m in M: L.append(m*2)" 
1000 loops, best of 3: 432 usec per loop 

2> python -m timeit -s "M=range(1000);f=lambda x: x*2" "L=map(f,M)" 
1000 loops, best of 3: 449 usec per loop 

3> python -m timeit -s "M=range(1000);f=lambda x:x*2" "L=[f(m) for m in M]" 
1000 loops, best of 3: 483 usec per loop 

4> python -m timeit -s "L=[]; A=L.append; M=range(1000)" "for m in M: A(m*2)" 
1000 loops, best of 3: 287 usec per loop  

5> python -m timeit -s "M=range(1000)" "L=[m*2 for m in M]" 
1000 loops, best of 3: 174 usec per loop 

Beachten Sie, dass alle bis auf die letzten beiden ähnlich sind. Es sind die Funktionsaufrufe (L.append oder f (x)), die das Timing stark beeinflussen. In # 4 wurde die L.append-Suche einmal im Setup durchgeführt. In # 5 wird eine Liste comp ohne Funktionsaufrufe verwendet.

+0

ich denke du beziehst dich auf meinen post. Ja, ich habe das schwerwiegende Problem gefunden, dass 'map' Iteratoren in py3k zurückgibt, aber ich denke nicht, dass etwas mit 'timeit' falsch ist, 'range' gibt Iteratoren zurück, so dass es wenig Auswirkungen hat, es nicht in die Setup-Phase zu bringen. – iamamac

+0

> python3 -m timeit "[m für m im Bereich (1000)]" 10000 Schleifen, am besten 3: 114 usc pro Schleife > python3 -m timeit -s M = Liste (Bereich (1000)) "[ m für m in M] " 10000 Schleifen, am besten 3: 83 Usec pro Schleife Es gibt einen signifikanten Unterschied beim Erstellen der Liste nur einmal. –

Verwandte Themen