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.


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)))

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.


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])

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))

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.


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]

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ć).


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


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)]))

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.



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.


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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.