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:
mapkann 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]?!
ohier 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 filterund reduceund alles aus dem itertoolsModul: 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.mapFunktion als reine abstrakte Funktion verwenden, während Sie eine funktionale Programmierung durchführen, bei der Sie Mapping mapoder Currying durchführen mapoder auf andere Weise davon profitieren, über mapeine Funktion zu sprechen . In Haskell beispielsweise fmapverallgemeinert 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]
forSchleife 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 mapund filterund ähnliche Funktionen (wie das sehr nützlich itertoolsModul) 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 mapim 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 fwir möchten map, und wir ignorieren die Faulheit, mapindem 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 (...), mapAUCH 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 disModuls 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 mapKlasse für die Demontage etwas undurchsichtig, aber wir können mit unserem Geschwindigkeitstest dafür sorgen.
mapund zu sagen filterzusammen mit Standardbibliothek itertoolssind 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 filtereine großartige Idee für Python 3 , und nur eine Rebellion anderer Pythonisten hielt sie im eingebauten Namespace (während sie reduceverschoben wurden functools). Ich persönlich bin anderer Meinung ( mapund bin filtermit vordefinierten, insbesondere eingebauten Funktionen einverstanden , benutze sie nur nie, wenn eine lambdabenö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, mapund filterwürde zu itertools(oder functools) oder ganz gehen, aber was auch immer der Fall ist, wenn man sagt, dass itertoolsdas 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, functoolsund operatorwurden alle speziell mit der funktionalen Programmierung konzipiert, und ich verwende funktionelle Idiome in Python die ganze Zeit), und itertoolsbietet Funktionen , die ein Schmerz selbst zu implementieren sein würde, es ist speziell mapund filterredundant mit Generator Ausdrücke das ließ Guido sie hassen. itertoolswar schon immer in Ordnung.
mapund filteranstelle 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 xs 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 mapund filter. Sie verhindern subtile, schwer zu diagnostizierende Fehler im Zusammenhang mit dem Bereich.
Vergessen Sie nicht, imapund ifilter(in itertools) in Betracht zu ziehen, ob sie für Ihre Situation geeignet sind!
mapund / 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 mapverhalten 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 mapeine einfache alte Liste zurückgegeben, genau wie es das Listenverständnis in beiden Sprachen tut. Der springende Punkt ist, dass der Rückgabewert von mapin Python 3 (und imapin 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 squaressieht es in der letzten print(list(squares))Zeile leer aus .
Zusammenfassen:
mapeine 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 lambdaAusdruck sein könnte.
Es gibt auch irgendwo ein Interview (ich kann es nicht ohne weiteres finden), in dem Guido lambdas 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.
constSchlüsselwort in C ++ ein großer Triumph in dieser Richtung.
lambdawurden 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.
zipfaul 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 mapein Listenverständnis vor - da die meisten asynchronen, parallelen oder verteilten Pakete eine mapFunktion zum Überladen von Pythons bieten map. Wenn Sie dann die entsprechende mapFunktion 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 listObjekt.
Wie bereits erwähnt , map()ist @AlexMartelli nur dann schneller als das Listenverständnis, wenn Sie keine lambdaFunktion verwenden.
Ich werde Ihnen einige Zeitvergleiche vorstellen.
Python 3.5.2 und CPython
Ich habe Jupiter-Notebook und insbesondere den %timeiteingebauten 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
listObjekt: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
listObjekt, 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 DummyNumwird berücksichtigt:
class DummyNum(object):
"""Dummy class"""
__slots__ = 'n',
def __init__(self, n):
self.n = n
def add(self):
self.n += 5
Insbesondere die addMethode. 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_comprehensionTechnik) 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.
mapist 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]. mapist 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 methodcallerOverhead 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 mapund ein Listenverständnis zu verwenden filter. Der Grund ist, dass das Listenverständnis klarer ist als mapund 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 lambdaAusdrücke als mapBedürfnisse. Darüber hinaus ermöglicht ein Verständnis auch eine einfache Filterung, während eine Filterung maperforderlich 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.
mapeine Liste zurückgegeben wird. In Python 3 wird mapes träge ausgewertet, sodass beim einfachen Aufrufen mapkeines der neuen Listenelemente berechnet wird, weshalb Sie so kurze Zeiten erhalten.