Gibt es einen Grund, lieber das map()
Listenverständnis zu verwenden oder umgekehrt? Ist einer von ihnen im Allgemeinen effizienter oder wird er im Allgemeinen als pythonischer angesehen als der andere?
Gibt es einen Grund, lieber das map()
Listenverständnis zu verwenden oder umgekehrt? Ist einer von ihnen im Allgemeinen effizienter oder wird er im Allgemeinen als pythonischer angesehen als der andere?
Antworten:
map
kann in einigen Fällen mikroskopisch schneller sein (wenn Sie KEIN Lambda für diesen Zweck herstellen, aber dieselbe Funktion in Map und Listcomp verwenden). In anderen Fällen kann das Listenverständnis schneller sein, und die meisten (nicht alle) Pythonisten betrachten sie als direkter und klarer.
Ein Beispiel für den winzigen Geschwindigkeitsvorteil der Karte bei genau derselben Funktion:
$ python -mtimeit -s'xs=range(10)' 'map(hex, xs)'
100000 loops, best of 3: 4.86 usec per loop
$ python -mtimeit -s'xs=range(10)' '[hex(x) for x in xs]'
100000 loops, best of 3: 5.58 usec per loop
Ein Beispiel dafür, wie der Leistungsvergleich vollständig umgekehrt wird, wenn die Karte ein Lambda benötigt:
$ python -mtimeit -s'xs=range(10)' 'map(lambda x: x+2, xs)'
100000 loops, best of 3: 4.24 usec per loop
$ python -mtimeit -s'xs=range(10)' '[x+2 for x in xs]'
100000 loops, best of 3: 2.32 usec per loop
map(operator.attrgetter('foo'), objs)
leichter zu lesen als [o.foo for o in objs]
?!
o
hier einzuführen , und Ihre Beispiele zeigen, warum.
str()
Beispiel einen Punkt hat .
Fälle
map
, obwohl sie als "unpythonisch" angesehen wird. Zum Beispiel map(sum, myLists)
ist eleganter / knapper als [sum(x) for x in myLists]
. Sie erhalten die Eleganz, keine Dummy-Variable (z. B. sum(x) for x...
oder sum(_) for _...
oder sum(readableName) for readableName...
) erstellen zu müssen, die Sie zweimal eingeben müssen, nur um zu iterieren. Das gleiche Argument gilt für filter
und reduce
und alles aus dem itertools
Modul: Wenn Sie bereits eine Funktion zur Hand haben, können Sie eine funktionale Programmierung durchführen. Dies verbessert die Lesbarkeit in einigen Situationen und verliert sie in anderen (z. B. unerfahrene Programmierer, mehrere Argumente) ... aber die Lesbarkeit Ihres Codes hängt in hohem Maße von Ihren Kommentaren ab.map
Funktion als reine abstrakte Funktion verwenden, während Sie eine funktionale Programmierung durchführen, bei der Sie Mapping map
oder Currying durchführen map
oder auf andere Weise davon profitieren, über map
eine Funktion zu sprechen . In Haskell beispielsweise fmap
verallgemeinert eine Funktorschnittstelle mit dem Namen Generalizing die Zuordnung über eine beliebige Datenstruktur. Dies ist in Python sehr ungewöhnlich, da die Python-Grammatik Sie dazu zwingt, im Generatorstil über Iteration zu sprechen. Sie können es nicht einfach verallgemeinern. (Dies ist manchmal gut und manchmal schlecht.) Sie können wahrscheinlich seltene Python-Beispiele finden, bei denen map(f, *lists)
es sinnvoll ist, dies zu tun. Das nächste Beispiel, das ich finden kann sumEach = partial(map,sum)
, ist ein Einzeiler, der in etwa gleichwertig ist mit:def sumEach(myLists):
return [sum(_) for _ in myLists]
for
Schleife verwenden : Sie können natürlich auch nur eine for-Schleife verwenden. Obwohl sie aus Sicht der funktionalen Programmierung nicht so elegant sind, machen nicht-lokale Variablen den Code in wichtigen Programmiersprachen wie Python manchmal klarer, da die Leute es sehr gewohnt sind, Code auf diese Weise zu lesen. For-Schleifen sind im Allgemeinen auch am effizientesten, wenn Sie lediglich eine komplexe Operation ausführen, bei der keine Liste erstellt wird, für die Listenverständnisse und Karten optimiert sind (z. B. Summieren oder Erstellen eines Baums usw.) - zumindest effizient in Bezug auf das Gedächtnis (nicht unbedingt in Bezug auf die Zeit, wo ich im schlimmsten Fall einen konstanten Faktor erwarten würde, abgesehen von einigen seltenen pathologischen Schluckauf bei der Müllabfuhr)."Pythonismus"
Ich mag das Wort "pythonic" nicht, weil ich nicht finde, dass pythonic in meinen Augen immer elegant ist. Dennoch map
und filter
und ähnliche Funktionen (wie das sehr nützlich itertools
Modul) sind wahrscheinlich unpythonic in Bezug auf Stil betrachtet.
Faulheit
In Bezug auf die Effizienz kann MAP , wie die meisten funktionalen Programmierkonstrukte, faul sein und ist in Python tatsächlich faul. Das bedeutet, dass Sie dies tun können (in Python3 ) und Ihr Computer nicht über genügend Speicher verfügt und alle nicht gespeicherten Daten verliert:
>>> map(str, range(10**100))
<map object at 0x2201d50>
Versuchen Sie dies mit einem Listenverständnis:
>>> [str(n) for n in range(10**100)]
# DO NOT TRY THIS AT HOME OR YOU WILL BE SAD #
Beachten Sie, dass Listenverständnisse ebenfalls von Natur aus faul sind, Python sie jedoch als nicht faul implementiert hat . Trotzdem unterstützt Python das Verständnis fauler Listen in Form von Generatorausdrücken wie folgt:
>>> (str(n) for n in range(10**100))
<generator object <genexpr> at 0xacbdef>
Sie können sich die [...]
Syntax grundsätzlich als Übergabe eines Generatorausdrucks an den Listenkonstruktor vorstellen list(x for x in range(5))
.
Kurzes erfundenes Beispiel
from operator import neg
print({x:x**2 for x in map(neg,range(5))})
print({x:x**2 for x in [-y for y in range(5)]})
print({x:x**2 for x in (-y for y in range(5))})
Listenverständnisse sind nicht faul und erfordern daher möglicherweise mehr Speicher (es sei denn, Sie verwenden Generatorverständnisse). Die eckigen Klammern [...]
machen die Dinge oft deutlich, besonders wenn sie in Klammern stehen. Auf der anderen Seite ist man manchmal wortreich wie beim Tippen [x for x in...
. Solange Sie Ihre Iteratorvariablen kurz halten, ist das Listenverständnis normalerweise klarer, wenn Sie Ihren Code nicht einrücken. Sie können Ihren Code jedoch jederzeit einrücken.
print(
{x:x**2 for x in (-y for y in range(5))}
)
oder aufbrechen:
rangeNeg5 = (-y for y in range(5))
print(
{x:x**2 for x in rangeNeg5}
)
Effizienzvergleich für Python3
map
ist jetzt faul:
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)'
1000000 loops, best of 3: 0.336 usec per loop ^^^^^^^^^
Wenn Sie daher nicht alle Ihre Daten verwenden oder nicht map
im Voraus wissen, wie viele Daten Sie in Python3 (und Generatorausdrücken in Python2 oder Python3) benötigen, wird die Berechnung ihrer Werte bis zum letzten erforderlichen Moment vermieden. Normalerweise überwiegt der Overhead bei der Verwendung map
. Der Nachteil ist, dass dies in Python im Gegensatz zu den meisten funktionalen Sprachen sehr begrenzt ist: Sie erhalten diesen Vorteil nur, wenn Sie von links nach rechts "in der richtigen Reihenfolge" auf Ihre Daten zugreifen, da Python-Generator-Ausdrücke nur in der Reihenfolge ausgewertet werden können x[0], x[1], x[2], ...
.
Nehmen wir jedoch an, wir haben eine vorgefertigte Funktion, die f
wir möchten map
, und wir ignorieren die Faulheit, map
indem wir sofort die Bewertung mit erzwingen list(...)
. Wir erhalten einige sehr interessante Ergebnisse:
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(map(f,xs))'
10000 loops, best of 3: 165/124/135 usec per loop ^^^^^^^^^^^^^^^
for list(<map object>)
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=[f(x) for x in xs]'
10000 loops, best of 3: 181/118/123 usec per loop ^^^^^^^^^^^^^^^^^^
for list(<generator>), probably optimized
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(f(x) for x in xs)'
1000 loops, best of 3: 215/150/150 usec per loop ^^^^^^^^^^^^^^^^^^^^^^
for list(<generator>)
Die Ergebnisse liegen in der Form AAA / BBB / CCC vor, bei der A mit einer Intel-Workstation von ca. 2010 mit Python 3 ausgeführt wurde.?.? Und B und C mit einer AMD-Workstation von ca. 2013 mit Python 3.2.1 durchgeführt wurden. mit extrem unterschiedlicher Hardware. Das Ergebnis scheint zu sein, dass das Karten- und Listenverständnis in der Leistung vergleichbar ist, was am stärksten von anderen Zufallsfaktoren beeinflusst wird. Das einzige, was wir sagen können, scheint zu sein, dass seltsamerweise, obwohl wir erwarten, dass Listenverständnisse [...]
besser funktionieren als Generatorausdrücke (...)
, map
AUCH effizienter als Generatorausdrücke sind (wiederum unter der Annahme, dass alle Werte ausgewertet / verwendet werden).
Es ist wichtig zu wissen, dass diese Tests eine sehr einfache Funktion (die Identitätsfunktion) annehmen. Dies ist jedoch in Ordnung, da bei einer komplizierten Funktion der Leistungsaufwand im Vergleich zu anderen Faktoren im Programm vernachlässigbar wäre. (Es kann immer noch interessant sein, mit anderen einfachen Dingen wie zu testen f=lambda x:x+x
)
Wenn Sie mit dem Lesen von Python-Assemblys vertraut sind, können Sie mithilfe des dis
Moduls feststellen , ob dies tatsächlich hinter den Kulissen geschieht:
>>> listComp = compile('[f(x) for x in xs]', 'listComp', 'eval')
>>> dis.dis(listComp)
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x2511a48, file "listComp", line 1>)
3 MAKE_FUNCTION 0
6 LOAD_NAME 0 (xs)
9 GET_ITER
10 CALL_FUNCTION 1
13 RETURN_VALUE
>>> listComp.co_consts
(<code object <listcomp> at 0x2511a48, file "listComp", line 1>,)
>>> dis.dis(listComp.co_consts[0])
1 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 18 (to 27)
9 STORE_FAST 1 (x)
12 LOAD_GLOBAL 0 (f)
15 LOAD_FAST 1 (x)
18 CALL_FUNCTION 1
21 LIST_APPEND 2
24 JUMP_ABSOLUTE 6
>> 27 RETURN_VALUE
>>> listComp2 = compile('list(f(x) for x in xs)', 'listComp2', 'eval')
>>> dis.dis(listComp2)
1 0 LOAD_NAME 0 (list)
3 LOAD_CONST 0 (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>)
6 MAKE_FUNCTION 0
9 LOAD_NAME 1 (xs)
12 GET_ITER
13 CALL_FUNCTION 1
16 CALL_FUNCTION 1
19 RETURN_VALUE
>>> listComp2.co_consts
(<code object <genexpr> at 0x255bc68, file "listComp2", line 1>,)
>>> dis.dis(listComp2.co_consts[0])
1 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 17 (to 23)
6 STORE_FAST 1 (x)
9 LOAD_GLOBAL 0 (f)
12 LOAD_FAST 1 (x)
15 CALL_FUNCTION 1
18 YIELD_VALUE
19 POP_TOP
20 JUMP_ABSOLUTE 3
>> 23 LOAD_CONST 0 (None)
26 RETURN_VALUE
>>> evalledMap = compile('list(map(f,xs))', 'evalledMap', 'eval')
>>> dis.dis(evalledMap)
1 0 LOAD_NAME 0 (list)
3 LOAD_NAME 1 (map)
6 LOAD_NAME 2 (f)
9 LOAD_NAME 3 (xs)
12 CALL_FUNCTION 2
15 CALL_FUNCTION 1
18 RETURN_VALUE
Es scheint besser zu sein, [...]
Syntax zu verwenden als list(...)
. Leider ist die map
Klasse für die Demontage etwas undurchsichtig, aber wir können mit unserem Geschwindigkeitstest dafür sorgen.
map
und zu sagen filter
zusammen mit Standardbibliothek itertools
sind von Natur aus schlechter Stil. Sofern GvR nicht tatsächlich sagt, dass sie entweder ein schrecklicher Fehler oder nur für die Leistung waren, ist die einzige natürliche Schlussfolgerung, wenn "Pythonicness" dies sagt, es als dumm zu vergessen ;-)
map
/ wäre filter
eine großartige Idee für Python 3 , und nur eine Rebellion anderer Pythonisten hielt sie im eingebauten Namespace (während sie reduce
verschoben wurden functools
). Ich persönlich bin anderer Meinung ( map
und bin filter
mit vordefinierten, insbesondere eingebauten Funktionen einverstanden , benutze sie nur nie, wenn eine lambda
benötigt wird), aber GvR nennt sie seit Jahren grundsätzlich nicht Pythonic.
itertools
? Der Teil, den ich aus dieser Antwort zitiere, ist die Hauptbehauptung, die mich verwirrt. Ich weiß nicht, ob in seiner idealen Welt, map
und filter
würde zu itertools
(oder functools
) oder ganz gehen, aber was auch immer der Fall ist, wenn man sagt, dass itertools
das in seiner Gesamtheit unpythonisch ist, dann weiß ich nicht wirklich, was "Pythonisch" ist soll bedeuten, aber ich glaube nicht, dass es etwas ähnliches sein kann wie "was GvR den Leuten empfiehlt".
map
/ angesprochen filter
, nicht itertools
. Funktionale Programmierung ist perfekt Pythonic ( itertools
, functools
und operator
wurden alle speziell mit der funktionalen Programmierung konzipiert, und ich verwende funktionelle Idiome in Python die ganze Zeit), und itertools
bietet Funktionen , die ein Schmerz selbst zu implementieren sein würde, es ist speziell map
und filter
redundant mit Generator Ausdrücke das ließ Guido sie hassen. itertools
war schon immer in Ordnung.
map
und filter
anstelle von Listenverständnissen verwenden.Ein objektiver Grund, warum Sie sie bevorzugen sollten, obwohl sie nicht "Pythonic" sind, ist folgender:
Sie benötigen Funktionen / Lambdas als Argumente, die einen neuen Bereich einführen .
Ich bin mehr als einmal davon gebissen worden:
for x, y in somePoints:
# (several lines of code here)
squared = [x ** 2 for x in numbers]
# Oops, x was silently overwritten!
aber wenn ich stattdessen gesagt hätte:
for x, y in somePoints:
# (several lines of code here)
squared = map(lambda x: x ** 2, numbers)
dann wäre alles in Ordnung gewesen.
Man könnte sagen, ich war dumm, denselben Variablennamen im selben Bereich zu verwenden.
Ich war nicht. Der Code war ursprünglich in Ordnung - die beiden x
s waren nicht im gleichen Bereich.
Erst nachdem ich den inneren Block in einen anderen Abschnitt des Codes verschoben hatte , trat das Problem auf (sprich: Problem während der Wartung, nicht Entwicklung), und ich habe es nicht erwartet.
Ja, wenn Sie diesen Fehler nie machen, ist das Listenverständnis eleganter.
Aber aus persönlicher Erfahrung (und weil ich gesehen habe, wie andere den gleichen Fehler gemacht haben) habe ich es so oft gesehen, dass ich denke, dass es den Schmerz nicht wert ist, den Sie durchmachen müssen, wenn sich diese Fehler in Ihren Code einschleichen.
Verwenden Sie map
und filter
. Sie verhindern subtile, schwer zu diagnostizierende Fehler im Zusammenhang mit dem Bereich.
Vergessen Sie nicht, imap
und ifilter
(in itertools
) in Betracht zu ziehen, ob sie für Ihre Situation geeignet sind!
map
und / oder filter
. Wenn überhaupt, ist die direkteste und logischste Übersetzung, um Ihr Problem zu vermeiden, nicht map(lambda x: x ** 2, numbers)
ein Generatorausdruck, list(x ** 2 for x in numbers)
der nicht leckt, wie JeromeJ bereits betont hat. Schauen Sie Mehrdad, nehmen Sie nicht so persönlich eine Ablehnung, ich bin nur stark anderer Meinung als Ihre Argumentation hier.
Tatsächlich map
verhalten sich Listenverständnisse in der Python 3-Sprache ganz anders. Schauen Sie sich das folgende Python 3-Programm an:
def square(x):
return x*x
squares = map(square, [1, 2, 3])
print(list(squares))
print(list(squares))
Sie können erwarten, dass die Zeile "[1, 4, 9]" zweimal gedruckt wird, aber stattdessen wird "[1, 4, 9]" gefolgt von "[]" gedruckt. Das erste Mal, wenn Sie es betrachten squares
, scheint es sich als eine Folge von drei Elementen zu verhalten, das zweite Mal als eine leere.
In der Python 2-Sprache wird map
eine einfache alte Liste zurückgegeben, genau wie es das Listenverständnis in beiden Sprachen tut. Der springende Punkt ist, dass der Rückgabewert von map
in Python 3 (und imap
in Python 2) keine Liste ist - es ist ein Iterator!
Die Elemente werden verbraucht, wenn Sie über einen Iterator iterieren, anders als wenn Sie über eine Liste iterieren. Deshalb squares
sieht es in der letzten print(list(squares))
Zeile leer aus .
Zusammenfassen:
map
eine Datenstruktur erzeugen, keinen Iterator. Aber vielleicht sind faule Iteratoren einfacher als faule Datenstrukturen. Denkanstöße. Danke @MnZrK
Ich finde, Listenverständnisse sind im Allgemeinen ausdrucksvoller für das, was ich versuche, als map
- beide schaffen es, aber das erstere erspart die mentale Belastung, zu versuchen, zu verstehen, was ein komplexer lambda
Ausdruck sein könnte.
Es gibt auch irgendwo ein Interview (ich kann es nicht ohne weiteres finden), in dem Guido lambda
s und die funktionalen Funktionen als das auflistet, was er am meisten bedauert, wenn er in Python aufgenommen wird, sodass Sie das Argument vorbringen können, dass sie aufgrund ihrer Tugend nicht pythonisch sind davon.
const
Schlüsselwort in C ++ ein großer Triumph in dieser Richtung.
lambda
wurden sie so lahm gemacht (keine Aussagen ..), dass sie schwierig zu bedienen und sowieso begrenzt sind.
Hier ist ein möglicher Fall:
map(lambda op1,op2: op1*op2, list1, list2)
gegen:
[op1*op2 for op1,op2 in zip(list1,list2)]
Ich vermute, dass zip () ein unglücklicher und unnötiger Aufwand ist, dem Sie sich hingeben müssen, wenn Sie darauf bestehen, Listenverständnisse anstelle der Karte zu verwenden. Wäre toll, wenn jemand dies positiv oder negativ klarstellt.
zip
faul machen, indem Sieitertools.izip
map(operator.mul, list1, list2)
. Auf diesen sehr einfachen Ausdrücken auf der linken Seite wird das Verständnis ungeschickt.
Wenn Sie asynchronen, parallelen oder verteilten Code schreiben möchten, ziehen Sie wahrscheinlich map
ein Listenverständnis vor - da die meisten asynchronen, parallelen oder verteilten Pakete eine map
Funktion zum Überladen von Pythons bieten map
. Wenn Sie dann die entsprechende map
Funktion an den Rest Ihres Codes übergeben, müssen Sie möglicherweise nicht Ihren ursprünglichen Seriencode ändern, damit er parallel ausgeführt wird (usw.).
Da Python 3 map()
ein Iterator ist, müssen Sie bedenken, was Sie benötigen: einen Iterator oder ein list
Objekt.
Wie bereits erwähnt , map()
ist @AlexMartelli nur dann schneller als das Listenverständnis, wenn Sie keine lambda
Funktion verwenden.
Ich werde Ihnen einige Zeitvergleiche vorstellen.
Python 3.5.2 und CPython
Ich habe Jupiter-Notebook und insbesondere den %timeit
eingebauten magischen Befehl verwendet.
Messungen : s == 1000 ms == 1000 * 1000 µs = 1000 * 1000 * 1000 ns
Installieren:
x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)]
i_list = list(range(1000))
Eingebaute Funktion:
%timeit map(sum, x_list) # creating iterator object
# Output: The slowest run took 9.91 times longer than the fastest.
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 277 ns per loop
%timeit list(map(sum, x_list)) # creating list with map
# Output: 1000 loops, best of 3: 214 µs per loop
%timeit [sum(x) for x in x_list] # creating list with list comprehension
# Output: 1000 loops, best of 3: 290 µs per loop
lambda
Funktion:
%timeit map(lambda i: i+1, i_list)
# Output: The slowest run took 8.64 times longer than the fastest.
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 325 ns per loop
%timeit list(map(lambda i: i+1, i_list))
# Output: 1000 loops, best of 3: 183 µs per loop
%timeit [i+1 for i in i_list]
# Output: 10000 loops, best of 3: 84.2 µs per loop
Es gibt auch einen Generatorausdruck, siehe PEP-0289 . Also dachte ich, es wäre nützlich, es zum Vergleich hinzuzufügen
%timeit (sum(i) for i in x_list)
# Output: The slowest run took 6.66 times longer than the fastest.
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 495 ns per loop
%timeit list((sum(x) for x in x_list))
# Output: 1000 loops, best of 3: 319 µs per loop
%timeit (i+1 for i in i_list)
# Output: The slowest run took 6.83 times longer than the fastest.
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 506 ns per loop
%timeit list((i+1 for i in i_list))
# Output: 10000 loops, best of 3: 125 µs per loop
list
Objekt:Verwenden Sie das Listenverständnis, wenn es sich um eine benutzerdefinierte Funktion handelt, und verwenden Sie es, list(map())
wenn eine integrierte Funktion vorhanden ist
list
Objekt, Sie brauchen nur ein iterierbares:Immer benutzen map()
!
Ich habe einen Schnelltest durchgeführt, bei dem drei Methoden zum Aufrufen der Methode eines Objekts verglichen wurden. Der Zeitunterschied ist in diesem Fall vernachlässigbar und hängt von der jeweiligen Funktion ab (siehe Antwort von @Alex Martelli ). Hier habe ich mir folgende Methoden angesehen:
# map_lambda
list(map(lambda x: x.add(), vals))
# map_operator
from operator import methodcaller
list(map(methodcaller("add"), vals))
# map_comprehension
[x.add() for x in vals]
Ich habe mir Listen (in der Variablen gespeichert vals
) von Ganzzahlen (Python int
) und Gleitkommazahlen (Python float
) angesehen, um die Listengröße zu erhöhen. Die folgende Dummy-Klasse DummyNum
wird berücksichtigt:
class DummyNum(object):
"""Dummy class"""
__slots__ = 'n',
def __init__(self, n):
self.n = n
def add(self):
self.n += 5
Insbesondere die add
Methode. Das __slots__
Attribut ist eine einfache Optimierung in Python, um den von der Klasse benötigten Gesamtspeicher (Attribute) zu definieren und die Speichergröße zu reduzieren. Hier sind die resultierenden Diagramme.
Wie bereits erwähnt, macht die verwendete Technik nur einen minimalen Unterschied, und Sie sollten auf eine Weise codieren, die für Sie am besten lesbar ist, oder unter bestimmten Umständen. In diesem Fall ist das Listenverständnis ( map_comprehension
Technik) für beide Arten von Ergänzungen in einem Objekt am schnellsten, insbesondere bei kürzeren Listen.
Besuchen Sie diesen Pastebin für die Quelle, aus der das Diagramm und die Daten generiert wurden.
map
ist es nur dann schneller, wenn die Funktion genauso aufgerufen wird (dh [*map(f, vals)]
vs. [f(x) for x in vals]
). Ist list(map(methodcaller("add"), vals))
also schneller als [methodcaller("add")(x) for x in vals]
. map
ist möglicherweise nicht schneller, wenn das Gegenstück zur Schleife eine andere aufrufende Methode verwendet, mit der ein gewisser Overhead x.add()
vermieden werden kann (z. B. vermeidet der methodcaller
Overhead des oder des Lambda-Ausdrucks). Für diesen speziellen Testfall [*map(DummyNum.add, vals)]
wäre schneller (weil DummyNum.add(x)
und x.add()
haben im Grunde die gleiche Leistung).
list()
Aufrufe sind übrigens etwas langsamer als das Listenverständnis. Für einen fairen Vergleich müssen Sie schreiben [*map(...)]
.
list()
Anrufe den Overhead erhöhten. Hätte mehr Zeit damit verbringen sollen, die Antworten durchzulesen. Ich werde diese Tests für einen fairen Vergleich wiederholen, wie vernachlässigbar die Unterschiede auch sein mögen.
Ich bin der Meinung, dass der pythonischste Weg darin besteht, anstelle von map
und ein Listenverständnis zu verwenden filter
. Der Grund ist, dass das Listenverständnis klarer ist als map
und filter
.
In [1]: odd_cubes = [x ** 3 for x in range(10) if x % 2 == 1] # using a list comprehension
In [2]: odd_cubes_alt = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 1, range(10)))) # using map and filter
In [3]: odd_cubes == odd_cubes_alt
Out[3]: True
Wie Sie sehen, erfordert ein Verständnis keine zusätzlichen lambda
Ausdrücke als map
Bedürfnisse. Darüber hinaus ermöglicht ein Verständnis auch eine einfache Filterung, während eine Filterung map
erforderlich ist filter
.
Ich habe den Code von @ alex-martelli ausprobiert, aber einige Unstimmigkeiten festgestellt
python -mtimeit -s "xs=range(123456)" "map(hex, xs)"
1000000 loops, best of 5: 218 nsec per loop
python -mtimeit -s "xs=range(123456)" "[hex(x) for x in xs]"
10 loops, best of 5: 19.4 msec per loop
Die Karte benötigt auch für sehr große Bereiche dieselbe Zeit, während die Verwendung des Listenverständnisses viel Zeit in Anspruch nimmt, wie aus meinem Code hervorgeht. Abgesehen davon, dass ich als "unpythonisch" eingestuft wurde, habe ich keine Leistungsprobleme im Zusammenhang mit der Verwendung der Karte festgestellt.
map
eine Liste zurückgegeben wird. In Python 3 wird map
es träge ausgewertet, sodass beim einfachen Aufrufen map
keines der neuen Listenelemente berechnet wird, weshalb Sie so kurze Zeiten erhalten.