Jak zrobić wiele argumentów, aby zmapować funkcję, w której jeden pozostaje taki sam w Pythonie?

155

Powiedzmy, że mamy funkcję add w następujący sposób

def add(x, y):
    return x + y

chcemy zastosować funkcję mapy dla tablicy

map(add, [1, 2, 3], 2)

Semantyka jest taka, że ​​chcę dodać 2 do każdego elementu tablicy. Ale mapfunkcja wymaga również listy w trzecim argumencie.

Uwaga: dodaję przykład dla uproszczenia. Moja pierwotna funkcja jest znacznie bardziej skomplikowana. I oczywiście opcja ustawienia domyślnej wartości yfunkcji dodawania nie wchodzi w grę, ponieważ będzie ona zmieniana przy każdym wywołaniu.

Shan
źródło
20
to jest dokładnie to samo, co w Lispie: map(add,[1,2,3],[2]*3) generalnie mapprzyjmuje funkcję jako pierwszy argument, a jeśli ta funkcja przyjmuje argument K , musisz kontynuować z K iterowalnym:addTriple(a,b,c) -> map(addTriple,[...],[...],[...])
watashiSHUN

Odpowiedzi:

188

Jedną z opcji jest zrozumienie listy:

[add(x, 2) for x in [1, 2, 3]]

Więcej możliwości:

a = [1, 2, 3]

import functools
map(functools.partial(add, y=2), a)

import itertools
map(add, a, itertools.repeat(2, len(a)))
Sven Marnach
źródło
1
tak, ale jak szybko jest w porównaniu z funkcją mapy?
Shan
@Shan: Bardzo podobne, zwłaszcza jeśli add()jest to nietrywialna funkcja
Sven Marnach
2
@Shan: Spójrz na NumPy w tym przypadku. Jeśli naprawdę stanowi to dla Ciebie problem, różnica w szybkości między składaniem list i w map()żadnym wypadku nie pomoże.
Sven Marnach
3
@Shan: Jak powiedziałem wcześniej, spójrz na NumPy. Może to pomóc znacznie przyspieszyć pętle, pod warunkiem, że można je wektoryzować.
Sven Marnach
1
@abarnert: Nie jest jasne, czy OP korzysta z Pythona 2 czy 3. Aby upewnić się, że przykład działa w Pythonie 2, dołączyłem parametr. (Zauważ, że map()zachowuje się jak zip_longest()w Pythonie 2, podczas gdy zachowuje się jak zip()w Pythonie 3.)
Sven Marnach
60

Dokumenty wyraźnie sugerują, że jest to główne zastosowanie itertools.repeat:

Stwórz iterator, który zwraca obiekt w kółko. Działa przez czas nieokreślony, chyba że określono argument times. Używany jako argument map()dla niezmiennych parametrów wywoływanej funkcji. Używany również w programie zip()do tworzenia niezmiennej części rekordu krotki.

I nie ma powodu, aby przekazać len([1,2,3])jako timesargument; mapzatrzymuje się, gdy tylko zostanie zużyta pierwsza iterowalna, więc nieskończona iteracja jest całkowicie w porządku:

>>> from operator import add
>>> from itertools import repeat
>>> list(map(add, [1,2,3], repeat(4)))
[5, 6, 7]

W rzeczywistości jest to równoważne z przykładem repeatw dokumentach:

>>> list(map(pow, range(10), repeat(2)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

To sprawia, że ​​jest to przyjemne, leniwe, funkcjonalne rozwiązanie w języku y, które jest również doskonale czytelne pod względem iteratorów Pythona.

abarnert
źródło
2
+1 To powinna być zaakceptowana odpowiedź. Rozciąga się oczywiście na dowolną liczbę parametrów, z niektórymi stałymi i innymi listami lub generatorami. Np .: def f(x,y,z): \\ return '.'.join([x,y,z])a potem: list(map(f, list('abc'), repeat('foo'), list('defgh')))wraca ['a.foo.d', 'b.foo.e', 'c.foo.f'].
Pierre D
5
W Pythonie 2 konieczne jest podanie argumentu length repeat(), ponieważ map()będzie działać do wyczerpania najdłuższego iteratora w tej wersji Pythona, uzupełniając Nonewszystkie brakujące wartości. Mówienie, że „nie ma powodu”, aby przekazać parametr, jest błędne.
Sven Marnach
Komentarz @SvenMarnach nie jest drugorzędny: zachowanie Pythona 3 i Pythona 2 znacznie się różni!
Agustín
36

Użyj rozumienia listy.

[x + 2 for x in [1, 2, 3]]

Jeśli naprawdę , naprawdę , naprawdę chcesz użyć map, daj mu anonimową funkcję jako pierwszy argument:

map(lambda x: x + 2, [1,2,3])
Fred Foo
źródło
16

Mapa może zawierać wiele argumentów, standardowym sposobem jest

map(add, a, b)

W twoim pytaniu tak powinno być

map(add, a, [2]*len(a))
Yi.ding
źródło
Nie jestem pewien, co oznaczało jego pytanie, ale myślę, że wartość add accept 2, a te 2 nie są ustalone i powinny pochodzić od użytkownika, takiego jak nadchodzi arg1. więc w tym przypadku jak powinniśmy wywołać add (x, y) pod mapą?
Bimlesh Sharma
15

Prawidłowa odpowiedź jest prostsza niż myślisz. Po prostu zrób:

map(add, [(x, 2) for x in [1,2,3]])

I zmień implementację add, aby wziąć krotkę tj

def add(t):
   x, y = t
   return x+y

Może to obsłużyć każdy skomplikowany przypadek użycia, w którym oba parametry dodawania są dynamiczne.

Simon Ndunda
źródło
14

Czasami rozwiązywałem podobne sytuacje (np. Używając metody pandas.apply ) używając domknięć

Aby z nich skorzystać, definiujesz funkcję, która dynamicznie definiuje i zwraca opakowanie dla Twojej funkcji, skutecznie czyniąc jeden z parametrów stałą.

Coś takiego:

def add(x, y):
   return x + y

def add_constant(y):
    def f(x):
        return add(x, y)
    return f

Następnie add_constant(y)zwraca funkcję, której można użyć do dodania ydo dowolnej podanej wartości:

>>> add_constant(2)(3)
5

Co pozwala na użycie go w każdej sytuacji, w której parametry podawane są pojedynczo:

>>> map(add_constant(2), [1,2,3])
[3, 4, 5]

edytować

Jeśli nie chcesz pisać funkcji zamknięcia w innym miejscu, zawsze masz możliwość zbudowania jej w locie za pomocą funkcji lambda:

>>> map(lambda x: add(x, 2), [1, 2, 3])
[3, 4, 5]
Carles Sala
źródło
13

Jeśli masz to dostępne, rozważyłbym użycie numpy. Jest bardzo szybki w przypadku tego typu operacji:

>>> import numpy
>>> numpy.array([1,2,3]) + 2
array([3, 4, 5])

Zakłada się, że twoja prawdziwa aplikacja wykonuje operacje matematyczne (które można wektoryzować).

jterrace
źródło
10

Jeśli naprawdę potrzebujesz użyć funkcji map (tak jak moje przypisanie klasy tutaj ...), możesz użyć funkcji opakowującej z 1 argumentem, przekazując resztę do oryginalnej w jej treści; tj .:

extraArguments = value
def myFunc(arg):
    # call the target function
    return Func(arg, extraArguments)


map(myFunc, itterable)

Brudny i brzydki, nadal działa

Todor Minakov
źródło
1
Zamknięcie, myślę, że to coś dodaje, plus jeden. Podobna do funkcji częściowej, jak ma przyjęta odpowiedź.
Aaron Hall
1
myFuncmusireturn Func(arg, extraArguments)
PM 2Ring
Stoję poprawiony @ PM2Ring, to prawda; zaktualizował kod.
Todor Minakov
gdzie zdefiniowano Func? ponieważ otrzymuję błąd nazwy dla Func
Nikhil Mishra
Functo nazwa twojej pierwotnej funkcji - dla PO napięty byłoby addna przykład
Todor Minakov
6

Wierzę, że Starmap jest tym, czego potrzebujesz:

from itertools import starmap


def test(x, y, z):
    return x + y + z

list(starmap(test, [(1, 2, 3), (4, 5, 6)]))
Shqi.Yang
źródło
literówka w Twoim przykładzie: list (starmap (test, [(1, 2, 3), (4, 5, 6)]))
Teebu
O tak. Dzięki :)
Shqi.Yang
1

Przekazywanie wielu argumentów do mapfunkcji.

def q(x,y):
    return x*y

print map (q,range(0,10),range(10,20))

Tutaj q jest funkcją z wieloma argumentami, którą wywołuje map () . Upewnij się, że długość obu zakresów tj

len (range(a,a')) and len (range(b,b')) are equal.
SeasonalShot
źródło
1

W :nums = [1, 2, 3]

W :map(add, nums, [2]*len(nums))

Na zewnątrz:[3, 4, 5]

张小诚
źródło
1
To nie daje odpowiedzi na pytanie. Możesz wyszukać podobne pytania lub zapoznać się z pytaniami pokrewnymi i połączonymi w prawej części strony, aby znaleźć odpowiedź. Jeśli masz podobne, ale inne pytanie, zadaj nowe pytanie i dołącz link do tego, aby uzyskać kontekst. Zobacz: Zadawaj pytania,
Tim Diekmann
1

Możesz dołączyć lambdę wraz z mapą:

list(map(lambda a: a+2, [1, 2, 3]))
Hanish Madan
źródło
1
def func(a, b, c, d):
 return a + b * c % d
map(lambda x: func(*x), [[1,2,3,4], [5,6,7,8]])

Zawijając wywołanie funkcji lambda i używając polecenia rozpakowywania gwiazdy, możesz wykonać mapowanie z dowolną liczbą argumentów.

Yuukio
źródło
lambda jest bardzo powolna, zamiast tego użyj functools
Wang
0

Inną opcją jest:

results = []
for x in [1,2,3]:
    z = add(x,2)
    ...
    results += [f(z,x,y)]

Ten format jest bardzo przydatny podczas wywoływania wielu funkcji.

sudoman
źródło