Ich bin daran interessiert, das neue Sprachdesign von Python 3.x zu verstehen .
In Python 2.7 gefällt mir die Funktion map
:
Python 2.7.12
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: [2, 3, 4]
In Python 3.x haben sich jedoch folgende Dinge geändert:
Python 3.5.1
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: <map at 0x4218390>
Ich verstehe das Wie, aber ich konnte keinen Hinweis auf das Warum finden. Warum haben die Sprachdesigner diese Wahl getroffen, was meiner Meinung nach sehr schmerzhaft ist? War dies ein Problem für Entwickler, sich an das Listenverständnis zu halten?
IMO, Liste kann natürlich als Functors gedacht werden ; und ich wurde irgendwie gedacht, um so zu denken:
fmap :: (a -> b) -> f a -> f b
python
python-3.x
NoIdeaHowToFixThis
quelle
quelle
map
einfach über das Ergebnis iteriert wurden. Das Erstellen einer Liste, wenn Sie sie nicht benötigen, ist ineffizient, daher haben die Entwickler beschlossen,map
faul zu sein. Hier gibt es viel zu gewinnen für die Leistung und nicht viel zu verlieren (Wenn Sie eine Liste benötigen, fragen Sie einfach nach einer ...list(map(...))
).Antworten:
Ich denke , der Grund , warum Karte noch existiert überhaupt , wenn Generator Ausdrücke auch vorhanden ist , ist , dass es mehrere Iterator Argumente annehmen kann , dass alle geschlungen über und ging in der Funktion sind:
>>> list(map(min, [1,2,3,4], [0,10,0,10])) [0,2,0,4]
Das ist etwas einfacher als die Verwendung von zip:
>>> list(min(x, y) for x, y in zip([1,2,3,4], [0,10,0,10]))
Andernfalls wird einfach nichts über Generatorausdrücke hinzugefügt.
quelle
c = list(map(max, [1,2,3,4], [0,10,0,10, 99]))
in Python 2 und in Python 3.Da ein Iterator zurückgegeben wird, wird das Speichern der Liste in voller Größe im Speicher weggelassen. Damit Sie in Zukunft problemlos darüber iterieren können, ohne das Gedächtnis zu belasten. Möglicherweise benötigen Sie nicht einmal eine vollständige Liste, sondern den Teil davon, bis Ihr Zustand erreicht ist.
Sie können diese Dokumente nützlich finden, Iteratoren sind fantastisch.
quelle
Guido beantwortet diese Frage hier : " Da das Erstellen einer Liste nur verschwenderisch wäre ".
Er sagt auch, dass die richtige Transformation darin besteht, eine reguläre
for
Schleife zu verwenden.Das Konvertieren
map()
von 2 in 3 ist möglicherweise nicht nur ein einfacher Fall, bei dem ein Wert darauf geklebtlist( )
wird. Guido sagt auch:"Wenn die Eingabesequenzen nicht gleich lang sind,
map()
wird sie am Ende der kürzesten der Sequenzen beendet. Um die vollständige Kompatibilität mitmap()
Python 2.x zu gewährleisten , wickeln Sie die Sequenzen auch initertools.zip_longest()
zmap(func, *sequences)
wird
list(map(func, itertools.zip_longest(*sequences)))
""
quelle
map()
wegen der Nebenwirkungen der Funktion aufgerufen , nicht wegen ihrer Verwendung als Funktor.zip_longest
ist falsch. Sie müssen verwenden,itertools.starmap
damit es gleichwertig ist :list(starmap(func, zip_longest(*sequences)))
. Das ist , weilzip_longest
Tupel erzeugt, so dass diefunc
ein einziges erhalten würden
-uple Argument stattn
unterschiedlicher Argumente wie es der Fall bei einem Anrufmap(func, *sequences)
.In Python 3 geben viele Funktionen (nicht nur
map
aberzip
, sondern auchrange
andere) einen Iterator anstelle der vollständigen Liste zurück. Möglicherweise möchten Sie einen Iterator (z. B. um zu vermeiden, dass die gesamte Liste im Speicher bleibt) oder eine Liste (z. B. um indizieren zu können).Ich denke jedoch, dass der Hauptgrund für die Änderung in Python 3 darin besteht, dass das Konvertieren eines Iterators in eine Liste mit
list(some_iterator)
dem umgekehrten Äquivalentiter(some_list)
zwar trivial ist, aber nicht das gewünschte Ergebnis erzielt, da die vollständige Liste bereits erstellt und im Speicher gespeichert wurde.In Python 3
list(range(n))
funktioniert dies beispielsweise einwandfrei, da dasrange
Erstellen und anschließende Konvertieren des Objekts in eine Liste nur geringe Kosten verursacht . In Python 2 wirditer(range(n))
jedoch kein Speicher gespeichert, da die vollständige Liste erstellt wird,range()
bevor der Iterator erstellt wird.Daher sind in Python 2 separate Funktionen erforderlich, um einen Iterator anstelle einer Liste zu erstellen, z. B.
imap
formap
(obwohl sie nicht ganz gleichwertig sind ),xrange
forrange
,izip
forzip
. Im Gegensatz dazu benötigt Python 3 nur eine einzige Funktion, da einlist()
Aufruf bei Bedarf die vollständige Liste erstellt.quelle
itertools
Rückkehriteratoren. Außerdem würde ich Iteratoren nicht als Lazy Lists betrachten, da Listen mehrfach iteriert und zufällig aufgerufen werden können.map
Objekte haben einenext()
Methode. Python 3range
Range Objekte sind keine strengen Iteratoren, die ich kennefoo = map(lambda x: x, [1, 2, 3])
ein Kartenobjekt zurückfoo
. Tunfoo.next()
kommt mit einem Fehler zurück:'map' object has no attribute 'next'
__
sind Python vorbehalten. Ohne diesen Vorbehalt haben Sie das Problem, Dinge zu unterscheiden, für dienext
es sich nur um eine Methode handelt (sie sind nicht wirklich Iteratoren), und Dinge, die Iteratoren sind. In der Praxis sollten Sie die Methoden überspringen und nur dienext()
Funktion (z. B.next(foo)
) verwenden, die ab 2.6 in jeder Python-Version ordnungsgemäß funktioniert. Es ist die gleiche Art und Weise, wie Sie es verwendenlen(foo)
, obwohlfoo.__len__()
es gut funktionieren würde. Die Dunder-Methoden sollen im Allgemeinen nicht direkt, sondern implizit als Teil einer anderen Operation aufgerufen werden.