¿Cómo hacer múltiples argumentos para asignar la función donde uno permanece igual en python?

155

Digamos que tenemos una función agregar de la siguiente manera

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

queremos aplicar la función de mapa para una matriz

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

La semántica es que quiero agregar 2 a cada elemento de la matriz. Pero la mapfunción también requiere una lista en el tercer argumento.

Nota: Estoy poniendo el ejemplo de agregar por simplicidad. Mi función original es mucho más complicada. Y, por supuesto, la opción de configurar el valor predeterminado de la yfunción in add está fuera de discusión, ya que se cambiará para cada llamada.

Shan
fuente
20
esto es exactamente lo mismo que en Lisp: map(add,[1,2,3],[2]*3) en general maptoma una función como su primer argumento, y si esta función toma un argumento K , debe seguir con K iterable:addTriple(a,b,c) -> map(addTriple,[...],[...],[...])
watashiSHUN

Respuestas:

188

Una opción es una lista de comprensión:

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

Mas opciones:

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
fuente
1
Sí, pero ¿qué tan rápido es en comparación con la función de mapa?
Shan
@Shan: Muy similar, especialmente si add()es una función no trivial
Sven Marnach
2
@Shan: Echa un vistazo a NumPy en este caso. Si esto realmente es un problema para usted, la diferencia de velocidad entre las comprensiones de la lista y map()no ayudará de ninguna manera.
Sven Marnach
3
@Shan: Como dije antes, echa un vistazo a NumPy. Podría ayudar a acelerar los bucles de manera condicional, siempre que puedan ser vectorizados.
Sven Marnach
1
@abarnert: No está claro si el OP está usando Python 2 o 3. Para asegurarme de que el ejemplo funciona en Python 2, incluí el parámetro. (Tenga en cuenta que se map()comporta como zip_longest()en Python 2, mientras que se comporta como zip()en Python 3.)
Sven Marnach
60

Los documentos sugieren explícitamente que este es el uso principal para itertools.repeat:

Haga un iterador que devuelva el objeto una y otra vez. Se ejecuta indefinidamente a menos que se especifique el argumento de tiempos. Se usa como argumento map()para los parámetros invariantes de la función llamada. También se usa con zip()para crear una parte invariable de un registro de tupla.

Y no hay razón para pasar len([1,2,3])como timesargumento; mapse detiene tan pronto como se consume el primer iterable, por lo que un iterable infinito está perfectamente bien:

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

De hecho, esto es equivalente al ejemplo de repeaten los documentos:

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

Esto lo convierte en una buena solución de lenguaje perezoso y funcional que también es perfectamente legible en términos de iterador de Python.

abarnert
fuente
2
+1 Esta debería ser la respuesta aceptada. Se extiende a cualquier número de parámetros, por supuesto, con algunas listas o generadores constantes y otros. Por ejemplo: def f(x,y,z): \\ return '.'.join([x,y,z])y luego: list(map(f, list('abc'), repeat('foo'), list('defgh')))regresa ['a.foo.d', 'b.foo.e', 'c.foo.f'].
Pierre D
55
En Python 2, es necesario proporcionar el argumento de longitud repeat(), ya que map()se ejecutará hasta que se agote el iterador más largo en esa versión de Python, completando Nonetodos los valores faltantes. Decir que "no hay razón" para pasar el parámetro está mal.
Sven Marnach
El comentario de @SvenMarnach no es menor: ¡el comportamiento de Python 3 y Python 2 varía drásticamente!
Agustín
36

Usa una lista de comprensión.

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

Si realmente , realmente , realmente quiere usar map, dele una función anónima como primer argumento:

map(lambda x: x + 2, [1,2,3])
Fred Foo
fuente
16

El mapa puede contener múltiples argumentos, la forma estándar es

map(add, a, b)

En su pregunta, debería ser

map(add, a, [2]*len(a))
Yi.ding
fuente
No estoy seguro de lo que significaba su pregunta, pero creo que agregar acepta el valor 2 y esos 2 no son fijos y deberían provenir del usuario como arg1 está llegando. Entonces, en este caso, ¿cómo deberíamos llamar a add (x, y) en el mapa?
Bimlesh Sharma
15

La respuesta correcta es más simple de lo que piensas. Simplemente haz:

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

Y cambie la implementación de add para tomar una tupla, es decir

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

Esto puede manejar cualquier caso de uso complicado donde ambos parámetros de adición son dinámicos.

Simon Ndunda
fuente
14

Algunas veces resolví situaciones similares (como usar el método pandas.apply ) usando cierres

Para usarlos, usted define una función que define y devuelve dinámicamente un contenedor para su función, convirtiendo efectivamente uno de los parámetros en una constante.

Algo como esto:

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

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

Luego, add_constant(y)devuelve una función que se puede usar para agregar ya cualquier valor dado:

>>> add_constant(2)(3)
5

Lo que le permite usarlo en cualquier situación en la que los parámetros se dan uno a la vez:

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

editar

Si no desea tener que escribir la función de cierre en otro lugar, siempre tiene la posibilidad de construirla sobre la marcha utilizando una función lambda:

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

Si lo tiene disponible, consideraría usar numpy. Es muy rápido para este tipo de operaciones:

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

Esto supone que su aplicación real está haciendo operaciones matemáticas (que se pueden vectorizar).

jterrace
fuente
10

Si realmente necesita usar la función de mapa (como mi asignación de clase aquí ...), podría usar una función de contenedor con 1 argumento, pasando el resto a la original en su cuerpo; es decir:

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


map(myFunc, itterable)

Sucio y feo, todavía funciona

Todor Minakov
fuente
1
Un cierre, creo que esto agrega algo, más uno. Similar a una función parcial, como lo ha hecho la respuesta aceptada.
Aaron Hall
1
myFuncnecesitareturn Func(arg, extraArguments)
PM 2Ring
Estoy corregido @ PM2Ring, eso es cierto; actualizado el código
Todor Minakov
¿Dónde se define Func? porque recibo un error de nombre para Func
Nikhil Mishra
Funces el nombre de su función original; para OP tight sería, addpor ejemplo
Todor Minakov
6

Creo que Starmap es lo que necesitas:

from itertools import starmap


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

list(starmap(test, [(1, 2, 3), (4, 5, 6)]))
Shqi.Yang
fuente
error tipográfico en su ejemplo: list (starmap (test, [(1, 2, 3), (4, 5, 6)]))
Teebu
Oh si. Gracias :)
Shqi.Yang
1

Para pasar múltiples argumentos a una mapfunción.

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

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

Aquí q es una función con múltiples argumentos que map () llama. Asegúrese de que la longitud de ambos rangos es decir

len (range(a,a')) and len (range(b,b')) are equal.
Tiro estacional
fuente
1

Puede incluir lambda junto con el mapa:

list(map(lambda a: a+2, [1, 2, 3]))
Hanish Madan
fuente
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]])

Al ajustar la llamada a la función con una lambda y usar el descomprimir estrella, puede hacer un mapa con un número arbitrario de argumentos.

Yuukio
fuente
lambda es muy lento, use functools en su lugar
Wang
0

Otra opción es:

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

Este formato es muy útil cuando se llaman múltiples funciones.

sudoman
fuente