Come fare più argomenti per mappare la funzione in cui uno rimane lo stesso in Python?


155

Diciamo che abbiamo una funzione aggiungere come segue

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

vogliamo applicare la funzione mappa per un array

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

La semantica è che voglio aggiungere 2 a ogni elemento dell'array. Ma la mapfunzione richiede anche un elenco nel terzo argomento.

Nota: sto aggiungendo l'esempio per semplicità. La mia funzione originale è molto più complicata. E ovviamente l'opzione di impostare il valore predefinito di yin funzione add è fuori discussione in quanto verrà modificata per ogni chiamata.


20
questo è esattamente lo stesso di Lisp: map(add,[1,2,3],[2]*3) in generale mapaccetta una funzione come primo argomento, e se questa funzione accetta l' argomento K , devi seguire K iterabile:addTriple(a,b,c) -> map(addTriple,[...],[...],[...])
watashiSHUN

Risposte:


188

Un'opzione è la comprensione di un elenco:

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

Più opzioni:

a = [1, 2, 3]

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

import itertools
map(add, a, itertools.repeat(2, len(a)))

1
sì, ma quanto è veloce rispetto alla funzione mappa?
Shan,

@Shan: molto simile, soprattutto se add()è una funzione non banale
Sven Marnach,

2
@Shan: dai un'occhiata a NumPy in questo caso. Se questo è davvero un problema per te, la differenza di velocità tra la comprensione dell'elenco e map()non aiuterà in entrambi i modi.
Sven Marnach,

3
@Shan: come ho detto prima, dai un'occhiata a NumPy. Potrebbe aiutare ad accelerare i loop in modo comprensibile, a condizione che possano essere vettorializzati.
Sven Marnach,

1
@abarnert: non è chiaro se l'OP stia usando Python 2 o 3. Per assicurarsi che l'esempio funzioni in Python 2, ho incluso il parametro. (Nota che map()si comporta come zip_longest()in Python 2, mentre si comporta come zip()in Python 3.)
Sven Marnach,

60

I documenti suggeriscono esplicitamente che questo è l'uso principale per itertools.repeat:

Crea un iteratore che restituisce ripetutamente l'oggetto. Viene eseguito indefinitamente a meno che non venga specificato l'argomento times. Utilizzato come argomento map()per i parametri invarianti della funzione chiamata. Utilizzato anche con zip()per creare una parte invariante di un record di tupla.

E non c'è motivo di passare len([1,2,3])come timesargomento; mapsi interrompe non appena viene consumato il primo iterabile, quindi un iterabile infinito va perfettamente bene:

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

In realtà, questo equivale all'esempio per repeatnei documenti:

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

Questo rende una bella soluzione lazy-funzionale-linguaggio-y che è anche perfettamente leggibile in termini di iteratore Python.


2
+1 Questa dovrebbe essere la risposta accettata. Si estende a qualsiasi numero di parametri, ovviamente, con alcune liste costanti e altre o generatori. Ad esempio: def f(x,y,z): \\ return '.'.join([x,y,z])e quindi: list(map(f, list('abc'), repeat('foo'), list('defgh')))ritorna ['a.foo.d', 'b.foo.e', 'c.foo.f'].
Pierre D,

5
In Python 2, è necessario fornire l'argomento lunghezza a repeat(), poiché map()verrà eseguito fino a quando l' iteratore più lungo non sarà esaurito in quella versione di Python, compilando Nonetutti i valori mancanti. Dire che "non c'è motivo" per passare il parametro è sbagliato.
Sven Marnach,

Il commento di @SvenMarnach non è secondario: il comportamento di Python 3 e Python 2 varia drasticamente!
Agustín,

36

Usa una comprensione dell'elenco.

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

Se davvero , davvero , vuoi davvero usare map, dagli una funzione anonima come primo argomento:

map(lambda x: x + 2, [1,2,3])

16

La mappa può contenere più argomenti, il modo standard è

map(add, a, b)

Nella tua domanda, dovrebbe essere

map(add, a, [2]*len(a))

Non sono sicuro di cosa significasse la sua domanda, ma penso che aggiunga accettare il valore 2 e quei 2 non sono fissi e dovrebbero venire dall'utente come arg1 sta arrivando. quindi in questo caso come dovremmo chiamare add (x, y) sotto map?
Bimlesh Sharma,

15

La risposta corretta è più semplice di quanto pensi. Fai semplicemente:

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

E cambia l'implementazione di add per prendere una tupla, ad es

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

Questo può gestire qualsiasi caso d'uso complicato in cui entrambi i parametri add sono dinamici.


14

A volte ho risolto situazioni simili (come l'uso del metodo pandas.apply ) usando le chiusure

Per usarli, si definisce una funzione che definisce e restituisce dinamicamente un wrapper per la propria funzione, rendendo efficace uno dei parametri una costante.

Qualcosa come questo:

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

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

Quindi, add_constant(y)restituisce una funzione che può essere utilizzata per aggiungere ya qualsiasi dato valore:

>>> add_constant(2)(3)
5

Ciò consente di utilizzarlo in qualsiasi situazione in cui i parametri vengono dati uno alla volta:

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

modificare

Se non vuoi scrivere la funzione di chiusura da qualche altra parte, hai sempre la possibilità di crearla al volo usando una funzione lambda:

>>> map(lambda x: add(x, 2), [1, 2, 3])
[3, 4, 5]

13

Se lo hai a disposizione, prenderei in considerazione l'utilizzo di numpy. È molto veloce per questi tipi di operazioni:

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

Questo presuppone che la tua vera applicazione stia facendo operazioni matematiche (che possono essere vettorializzate).


10

Se hai davvero bisogno di usare la funzione mappa (come il mio compito di classe qui ...), potresti usare una funzione wrapper con 1 argomento, passando il resto a quello originale nel suo corpo; cioè:

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


map(myFunc, itterable)

Sporco e brutto, fa ancora il trucco


1
Una chiusura, penso che questo aggiunga qualcosa, più uno. Simile a una funzione parziale, come ha la risposta accettata.
Aaron Hall

1
myFuncdevereturn Func(arg, extraArguments)
PM 2Ring

Sono corretto @ PM2Ring, è vero; aggiornato il codice.
Todor Minakov,

dove è definito Func? perché sto ricevendo un errore di nome per Func
Nikhil Mishra

Funcè il nome della tua funzione originale - per OP stretto sarebbe addper esempio
Todor Minakov

6

Credo che lo starmap sia ciò di cui hai bisogno:

from itertools import starmap


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

list(starmap(test, [(1, 2, 3), (4, 5, 6)]))

errore di battitura nel tuo esempio: list (starmap (test, [(1, 2, 3), (4, 5, 6)]))
Teebu

Oh si. Grazie :)
Shqi.Yang

1

Passare più argomenti a una mapfunzione.

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

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

Qui q è una funzione con più argomenti che map () chiama. Assicurarsi che la lunghezza di entrambe le gamme es

len (range(a,a')) and len (range(b,b')) are equal.


1

Puoi includere lambda insieme alla mappa:

list(map(lambda a: a+2, [1, 2, 3]))

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

Avvolgendo la chiamata di funzione con un lambda e usando la stella unpack, puoi fare la mappa con un numero arbitrario di argomenti.


lambda è molto lento, usa invece functools
Wang

0

Un'altra opzione è:

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

Questo formato è molto utile quando si chiamano più funzioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.