Jestem zainteresowany w zrozumieniu nowej konstrukcji języka Python 3.x .
Podoba mi się funkcja w Pythonie 2.7 map
:
Python 2.7.12
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: [2, 3, 4]
Jednak w Pythonie 3.x sytuacja uległa zmianie:
Python 3.5.1
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: <map at 0x4218390>
Rozumiem jak, ale nie mogłem znaleźć odniesienia do przyczyny. Dlaczego projektanci języka dokonali takiego wyboru, który moim zdaniem wprowadza wiele bólu. Czy to było dla twórców siłowania się na rękę, którzy trzymali się listy zrozumień?
IMO, listę można oczywiście traktować jako funktory ; i jakoś sądzono, że myślę w ten sposób:
fmap :: (a -> b) -> f a -> f b
python
python-3.x
NoIdeaHowToFixThis
źródło
źródło
map
po prostu powtarzała się po wyniku. Tworzenie listy, gdy jej nie potrzebujesz, jest nieefektywne, więc twórcy postanowili sięmap
rozlenić. Wiele można tu zyskać za wydajność, a nie wiele do stracenia (jeśli potrzebujesz listy, poproś o nią ...list(map(...))
).Odpowiedzi:
Myślę, że jest to powód, dla którego mapa nadal istnieje w ogóle , gdy wyrażenia generatorów również istnieje, jest to, że może to potrwać wiele argumentów iteratorów, które są zapętlone nad i przekazywane do funkcji:
>>> list(map(min, [1,2,3,4], [0,10,0,10])) [0,2,0,4]
To trochę łatwiejsze niż używanie zip:
>>> list(min(x, y) for x, y in zip([1,2,3,4], [0,10,0,10]))
W przeciwnym razie po prostu nie dodaje niczego ponad wyrażeniami generatora.
źródło
c = list(map(max, [1,2,3,4], [0,10,0,10, 99]))
w Pythonie 2 i Pythonie 3.Ponieważ zwraca iterator, pomija przechowywanie listy pełnego rozmiaru w pamięci. Abyś mógł z łatwością powtarzać to w przyszłości, nie powodując żadnego bólu w pamięci. Prawdopodobnie nie potrzebujesz nawet pełnej listy, ale jej części, aż do osiągnięcia stanu.
Możesz znaleźć te dokumenty przydatne, iteratory są niesamowite.
źródło
Guido odpowiada tutaj na to pytanie : „ ponieważ tworzenie listy byłoby po prostu marnotrawstwem ”.
Mówi też, że właściwą transformacją jest użycie regularnej
for
pętli.Konwersja
map()
z 2 na 3 może nie być prostym przypadkiem naklejenialist( )
wokół niej znaku . Guido mówi również:„Jeśli sekwencje wejściowe nie są równej długości,
map()
zatrzyma się po zakończeniu najkrótszej z sekwencji. Aby uzyskać pełną zgodność zmap()
wersją z języka Python 2.x, należy również zawijać sekwencjeitertools.zip_longest()
, np.map(func, *sequences)
staje się
list(map(func, itertools.zip_longest(*sequences)))
"
źródło
map()
skutków ubocznych funkcji , a nie jej użycia jako funktora.zip_longest
jest zła. trzeba użyćitertools.starmap
, aby była ona równoważne:list(starmap(func, zip_longest(*sequences)))
. Dzieje sięzip_longest
tak, ponieważ tworzy krotki, więcfunc
otrzyma pojedynczyn
argument zamiastn
oddzielnych argumentów, jak ma to miejsce podczas wywoływaniamap(func, *sequences)
.W Pythonie 3 wiele funkcji (nie tylko
map
alezip
,range
i inni) zwraca iterator zamiast pełnej listy. Możesz potrzebować iteratora (np. Aby uniknąć trzymania całej listy w pamięci) lub możesz chcieć listy (np. Aby móc indeksować).Jednak myślę, że głównym powodem zmiany w Pythonie 3 jest to, że chociaż konwersja iteratora na listę przy użyciu
list(some_iterator)
odwrotnego odpowiednika jest trywialna,iter(some_list)
nie daje pożądanego rezultatu, ponieważ pełna lista została już zbudowana i przechowywana w pamięci.Na przykład w Pythonie 3
list(range(n))
działa dobrze, ponieważ zbudowanierange
obiektu, a następnie przekonwertowanie go na listę, kosztuje niewiele . Jednak w Pythonie 2iter(range(n))
nie oszczędza pamięci, ponieważ pełna lista jest tworzona przezrange()
przed zbudowaniem iteratora.Dlatego w Pythonie 2 do utworzenia iteratora wymagane są oddzielne funkcje, a nie lista, na przykład
imap
formap
(chociaż nie są one do końca równoważne ),xrange
forrange
,izip
forzip
. Natomiast Python 3 wymaga tylko jednej funkcji, ponieważlist()
wywołanie tworzy pełną listę, jeśli jest to wymagane.źródło
itertools
iteratorów powrotu. Ponadto nie postrzegałbym iteratorów jako leniwych list, ponieważ listy można iterować wiele razy i uzyskać do nich losowy dostęp.map
Obiekty Pythona 3 mająnext()
metodę.range
Obiekty zakresu Pythona 3 nie są ściśle iteratorami, które znamfoo = map(lambda x: x, [1, 2, 3])
zwraca obiekt mapyfoo
. działaniefoo.next()
wraca z błędem:'map' object has no attribute 'next'
__
są zarezerwowane dla Pythona; bez tego zastrzeżenia, masz problem z rozróżnieniem rzeczy, którenext
są tylko metodą (nie są one tak naprawdę iteratorami) i rzeczy, które są iteratorami. W praktyce należy pominąć metody i po prostu skorzystać znext()
funkcji (np.next(foo)
), Która działa poprawnie na każdej wersji Pythona od 2.6. Jest to ten sam sposób, w jaki używasz,len(foo)
chociażfoo.__len__()
działałoby dobrze; że Dunder metody są zazwyczaj przeznaczone nie być wywoływane bezpośrednio, lecz pośrednio w ramach innej operacji.