Zrozumienie funkcji mapy

311
map(function, iterable, ...)

Zastosuj funkcję do każdego elementu iterowalnego i zwróć listę wyników. Jeśli przekazane zostaną dodatkowe iterowalne argumenty, funkcja musi przyjąć tyle argumentów i zostanie zastosowana do elementów ze wszystkich iteracyjnych równolegle.

Jeśli jeden iterowalny jest krótszy od drugiego, zakłada się, że zostanie przedłużony o Brak elementów.

Jeśli funkcja jest None, to zakłada się funkcję tożsamości; jeśli istnieje wiele argumentów, map()zwraca listę składającą się z krotek zawierających odpowiednie elementy ze wszystkich iteracji (rodzaj operacji transpozycji).

Argumenty iterowalne mogą być sekwencją lub dowolnym obiektem iterowalnym; wynikiem jest zawsze lista.

Jaką rolę odgrywa to w tworzeniu produktu kartezjańskiego?

content = map(tuple, array)

Jaki efekt ma umieszczenie krotki w dowolnym miejscu? Zauważyłem również, że bez funkcji mapy wyjście jest, abca wraz z nim jest a, b, c.

Chcę w pełni zrozumieć tę funkcję. Definicje referencyjne są również trudne do zrozumienia. Za dużo fantazyjnego puchu.

Web Master
źródło
2
Co tak naprawdę chcesz osiągnąć i dlaczego konkretnie chcesz użyć map?
Kris Harper
3
@WebMaster tak, według pierwszego zdania w wklejonej dokumentacji - „Zastosuj funkcję do każdego elementu iterowalnego”. Reszta akapitu dotyczy bardziej skomplikowanych przypadków - jak się map(None, a, b, c)okazuje zip(a, b, c). Ale bardzo rzadko widzisz to w praktyce, właśnie dlatego, że zippołączenie jest równoważne.
lvc
9
Staram się nauczyć języka Python i zawsze, gdy otwieram definicję w python.org. po pierwszym zdaniu nic nie rozumiem. W porządku. Dziękuję Ci.
Web Master
2
tuplejest funkcją (cóż, jest bardziej dopracowana, ale zachowuje się jak funkcja), która wymaga iteracji i daje krotkę z tymi samymi elementami - więc tuple([1, 2, 3])jest to równoważne z (1, 2, 3). Za map(tuple, array), arraybyłoby iterable z iterables (myślę listę list), a to daje kopię każdej listy wewnętrzną zamienił krotki.
lvc
1
Ogólnie rzecz biorąc, najważniejsze jest pierwsze zdanie dokumentacji dowolnej funkcji. Jeśli to zrozumiesz, dostaniesz sedno. Reszta to określa zachowanie w najdrobniejszych szczegółach, a niektóre, że będzie być nieco nieprzejrzysty na początek, a może trzeba natknąć nieparzystej idiomu opartego na nim, zanim zobaczysz „Och, która jest , co to znaczy!”. Ale kiedy dostaniesz ten moment żarówki na kilka wbudowanych poleceń, powinieneś zacząć być w stanie nieco łatwiej zrozumieć dokumenty.
lvc

Odpowiedzi:

441

mapnie jest szczególnie pytoniczny. Poleciłbym zamiast tego używać wyrażeń listowych:

map(f, iterable)

jest w zasadzie równoważny z:

[f(x) for x in iterable]

mapsam w sobie nie może zrobić produktu kartezjańskiego, ponieważ długość jego listy wyników jest zawsze taka sama jak listy danych wejściowych. Możesz trywialnie zrobić kartezjański produkt z listą ze zrozumieniem:

[(a, b) for a in iterable_a for b in iterable_b]

Składnia jest nieco myląca - co w zasadzie odpowiada:

result = []
for a in iterable_a:
    for b in iterable_b:
        result.append((a, b))
Dave
źródło
36
Uważam, że używam mapdużo mniej szczegółowych słów niż listy, przynajmniej w przypadku, który demonstrujesz.
marbel
1
Jak korzystać z mapy dla właściwości? Co to jest mapekwiwalent [v.__name__ for v in (object, str)]?
A Sz
@ASz Co powiesz na map(lambda v: v.__name__, list)?
Kilian
10
mapa jest szybsza, ponieważ nie wywołuje funkcji opartych na długości iteratorów .. funkcje wywołujące mają narzut .. Obejrzyj 6:00 youtube.com/watch?v=SiXyyOA6RZg&t=813s
anati
1
@anati Myślałem, że czasamimap było szybsze niż rozumienie, a czasem nie, właśnie z powodu narzutu wywołania funkcji? W szczególności, heurystyka, której się nauczyłem, polega na tym, że kiedy używanie wymaga wprowadzenia dodatkowego wywołania funkcji, rozumienie jest szybsze? Np. Doprowadzono mnie do przekonania, że jest wolniejszy niż , a nawet wolniejszy niż , właśnie z powodu wywołania dodatkowej funkcji. mapmap(lambda foo: foo.bar, my_list)foo.bar for foo in my_listmap(operator.add, my_list_of_pairs)x + y for x, y in my_list_of_pairs
mtraceur
86

mapwcale nie odnosi się do produktu kartezjańskiego, chociaż wyobrażam sobie, że ktoś dobrze zorientowany w programowaniu funkcjonalnym mógłby wymyślić jakiś niemożliwy do zrozumienia sposób wygenerowania takiego map.

map w Pythonie 3 jest równoważne z tym:

def map(func, iterable):
    for i in iterable:
        yield func(i)

a jedyną różnicą w Pythonie 2 jest to, że utworzy pełną listę wyników, które zostaną zwrócone naraz zamiast yielding.

Chociaż konwencja Python zwykle preferuje wyrażenia listowe (lub wyrażenia generatora), aby uzyskać ten sam wynik, co wywołanie map, szczególnie jeśli używasz wyrażenia lambda jako pierwszego argumentu:

[func(i) for i in iterable]

Jako przykład tego, o co prosiłeś w komentarzach do pytania - „zamień ciąg znaków na tablicę”, przez „tablicę” prawdopodobnie potrzebujesz krotki lub listy (oba zachowują się trochę jak tablice z innych języków) -

 >>> a = "hello, world"
 >>> list(a)
['h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd']
>>> tuple(a)
('h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd')

Zastosowanie maptu będzie, jeśli zaczniesz z listy ciągów zamiast pojedynczego łańcucha - mapmoże listify wszystkie z nich z osobna:

>>> a = ["foo", "bar", "baz"]
>>> list(map(list, a))
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]

Zauważ, że map(list, a)jest to równoważne w Pythonie 2, ale w Pythonie 3 potrzebujesz listwywołania, jeśli chcesz zrobić coś innego niż nakarmić go w forpętli (lub takiej funkcji przetwarzania, sumktóra potrzebuje tylko iterowalnej, a nie sekwencji). Ale jeszcze raz zauważ, że zwykle preferowane jest zrozumienie listy:

>>> [list(b) for b in a]
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]
lvc
źródło
mapa (zabawa x -> (x, x)) nie wydaje się trudna do zrozumienia ... (chociaż uzyskanie prawdziwego kartezjańskiego produktu z mapy byłoby niemożliwe, wszystko, co tworzy mapa, jest zawsze jakąś formą listy)
Kristopher Micinski
36

map tworzy nową listę poprzez zastosowanie funkcji do każdego elementu źródła:

xs = [1, 2, 3]

# all of those are equivalent — the output is [2, 4, 6]
# 1. map
ys = map(lambda x: x * 2, xs)
# 2. list comprehension
ys = [x * 2 for x in xs]
# 3. explicit loop
ys = []
for x in xs:
    ys.append(x * 2)

n-ary mapjest równoważne skompresowaniu iterujących danych wejściowych razem, a następnie zastosowaniu funkcji transformacji na każdym elemencie tej pośredniej spakowanej listy. To nie jest produkt kartezjański:

xs = [1, 2, 3]
ys = [2, 4, 6]

def f(x, y):
    return (x * 2, y // 2)

# output: [(2, 1), (4, 2), (6, 3)]
# 1. map
zs = map(f, xs, ys)
# 2. list comp
zs = [f(x, y) for x, y in zip(xs, ys)]
# 3. explicit loop
zs = []
for x, y in zip(xs, ys):
    zs.append(f(x, y))

Użyłem ziptutaj, ale mapzachowanie faktycznie różni się nieznacznie, gdy iteratory nie są tego samego rozmiaru - jak zauważono w jego dokumentacji, rozszerza iterabie, aby zawierały None.

Cat Plus Plus
źródło
1
skomplikowane, próba przetworzenia tego postu
Web Master
1
@WebMaster Co jest w tym skomplikowanego?
Jossie Calderon,
Najlepsza odpowiedź według mnie. Użycie lambda w tym przykładzie jako funkcji jest bardzo jasne.
sheldonzy
Niestety, wszystkie z nich nie są równoważne - dane wyjściowe dotyczą [2,4,6]zrozumienia listy i jawnych pętli, ale mapa zwraca obiekt mapy - na przykład otrzymuję to: <map at 0x123a49978>co następnie muszę przymusić do listy.
leerssej,
20

Upraszczając trochę, możesz sobie wyobrazić map()zrobienie czegoś takiego:

def mymap(func, lst):
    result = []
    for e in lst:
        result.append(func(e))
    return result

Jak widać, pobiera funkcję i listę i zwraca nową listę, w wyniku czego zastosowanie funkcji do każdego elementu na liście danych wejściowych. Powiedziałem „trochę upraszczając”, ponieważ w rzeczywistości map()można przetworzyć więcej niż jeden iterowalny:

Jeśli przekazane zostaną dodatkowe iterowalne argumenty, funkcja musi przyjąć tyle argumentów i zostanie zastosowana do elementów ze wszystkich iteracyjnych równolegle. Jeśli jeden iterowalny jest krótszy od drugiego, zakłada się, że zostanie przedłużony o Brak elementów.

Druga część pytania: Jaką rolę odgrywa to w tworzeniu produktu kartezjańskiego? cóż, map() można go użyć do wygenerowania kartezjańskiego produktu z listy takiej jak ta:

lst = [1, 2, 3, 4, 5]

from operator import add
reduce(add, map(lambda i: map(lambda j: (i, j), lst), lst))

... Ale prawdę mówiąc, używanie product()jest znacznie prostszym i naturalnym sposobem rozwiązania problemu:

from itertools import product
list(product(lst, lst))

Tak czy inaczej, wynikiem jest iloczyn kartezjański lstzdefiniowany powyżej:

[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5),
 (2, 1), (2, 2), (2, 3), (2, 4), (2, 5),
 (3, 1), (3, 2), (3, 3), (3, 4), (3, 5),
 (4, 1), (4, 2), (4, 3), (4, 4), (4, 5),
 (5, 1), (5, 2), (5, 3), (5, 4), (5, 5)]
Óscar López
źródło
17

Ta map()funkcja służy do zastosowania tej samej procedury do każdego elementu w iterowalnej strukturze danych, takiej jak listy, generatory, ciągi i inne rzeczy.

Spójrzmy na przykład: map()można iterować każdy element na liście i zastosować funkcję do każdego elementu, niż zwróci (zwróci) nową listę.

Wyobraź sobie, że masz funkcję, która pobiera liczbę, dodaje 1 do tej liczby i zwraca:

def add_one(num):
  new_num = num + 1
  return new_num

Masz również listę liczb:

my_list = [1, 3, 6, 7, 8, 10]

jeśli chcesz zwiększyć liczbę na liście, możesz wykonać następujące czynności:

>>> map(add_one, my_list)
[2, 4, 7, 8, 9, 11]

Uwaga: Przynajmniej map()potrzebuje dwóch argumentów. Najpierw nazwa funkcji, a po drugie coś w rodzaju listy.

Zobaczmy inne fajne rzeczy, które map()można zrobić. map()może pobierać wiele iteracyjnych (list, ciągów itp.) i przekazywać element z każdej iterowalnej funkcji jako argument.

Mamy trzy listy:

list_one = [1, 2, 3, 4, 5]
list_two = [11, 12, 13, 14, 15]
list_three = [21, 22, 23, 24, 25]

map() może uczynić cię nową listą, która zawiera dodawanie elementów o określonym indeksie.

Teraz pamiętaj map(), potrzebuje funkcji. Tym razem użyjemy wbudowanej sum()funkcji. Uruchomienie map()daje następujący wynik:

>>> map(sum, list_one, list_two, list_three)
[33, 36, 39, 42, 45]

PAMIĘTAJ:
W Pythonie 2 map(), będzie iterował (przechodził przez elementy list) zgodnie z najdłuższą listą i przejdzie Nonedo funkcji dla krótszych list, więc twoja funkcja powinna je wyszukać Nonei obsłużyć, w przeciwnym razie wystąpią błędy. W Python 3 map()zatrzyma się po zakończeniu najkrótszej listy. Ponadto w Pythonie 3 map()zwraca iterator, a nie listę.

BlooB
źródło
8

Python3 - mapa (func, iterable)

Jedną rzeczą, o której nie wspomniano całkowicie (chociaż wspomniał o tym @BlooB) jest to, że mapa zwraca obiekt mapy NIE listę. Jest to duża różnica, jeśli chodzi o wydajność czasu podczas inicjalizacji i iteracji. Rozważ te dwa testy.

import time
def test1(iterable):
    a = time.clock()
    map(str, iterable)
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


def test2(iterable):
    a = time.clock()
    [ x for x in map(str, iterable)]
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


test1(range(2000000))  # Prints ~1.7e-5s   ~8s
test2(range(2000000))  # Prints ~9s        ~8s

Jak widać, inicjalizacja funkcji mapy nie zajmuje prawie wcale czasu. Jednak iteracja przez obiekt mapy trwa dłużej niż zwykła iteracja przez iterowalny. Oznacza to, że funkcja przekazana do map () nie jest stosowana do każdego elementu, dopóki element nie zostanie osiągnięty w iteracji. Jeśli chcesz listę, skorzystaj ze zrozumienia listy. Jeśli planujesz iterować w pętli for i w pewnym momencie pęknie, użyj mapy.

Ranga
źródło