¿Pasar funciones con argumentos a otra función en Python?

205

¿Es posible pasar funciones con argumentos a otra función en Python?

Di algo como:

def perform(function):
    return function()

Pero las funciones que se pasarán tendrán argumentos como:

action1()
action2(p)
action3(p,r)
Joan Venge
fuente

Respuestas:

291

¿Quieres decir esto?

def perform( fun, *args ):
    fun( *args )

def action1( args ):
    something

def action2( args ):
    something

perform( action1 )
perform( action2, p )
perform( action3, p, r )
S.Lott
fuente
10
¿Qué pasa con los parámetros con nombre? Es decir, def action1(arg1, arg2=None, arg3=None)¿cómo podría pasar un argumento que pretende asignar a arg3, por ejemplo?
ChaimKut
66
realizar (diversión, ** argumentos), ver stackoverflow.com/questions/8954746/…
Mannaggia el
¿Qué pasa si performy action1, action2en diferentes archivos? @ S. Lott
Alper
@alper importar ellas
pfabri
122

Para esto está lambda:

def Perform(f):
    f()

Perform(lambda: Action1())
Perform(lambda: Action2(p))
Perform(lambda: Action3(p, r))
Dave
fuente
77
También por curiosidad, ¿podría decirme por qué las lambdas no son buenas para este caso?
Joan Venge
11
Las lambdas son una de las mejores características de los buenos lenguajes de programación. desafortunadamente, la implementación de Python es severamente limitada. en este caso, sin embargo, que encajan perfectamente
Javier
3
Encuentro que la sintaxis limitada es casi opaca; son difíciles de explicar a n00bz. Sí, funcionan aquí, y las características confusas de la sintaxis están ausentes. Este es, quizás, el único ejemplo que he visto de una lambda que no es oscura.
S.Lott
11
Para que pueda recuperar el resultado de la función pasada, ¿no sería mejor si Perform () llamara "return f ()" en lugar de simplemente llamar f ().
mhawke
Creo que la versión lambda es bastante ordenada, pero curiosamente en las pruebas que ejecuté fue más lento llamar funciones a través de lambda que por el método fn (* args) discutido en otra respuesta.
Richard Shepherd
39

Puede usar la función parcial de functools de esta manera.

from functools import partial

def perform(f):
    f()

perform(Action1)
perform(partial(Action2, p))
perform(partial(Action3, p, r))

También funciona con palabras clave

perform(partial(Action4, param1=p))
nulo
fuente
1
functools.partialtambién es más versátil si performnecesita entregar más parámetros f. Por ejemplo, uno podría llamar perform(partial(Action3, p))y perform(f)hacer algo así f("this is parameter r").
Robert
13

¡Usa functools.partial, no lambdas! Y ofc Perform es una función inútil, puede pasar funciones directamente.

for func in [Action1, partial(Action2, p), partial(Action3, p, r)]:
  func()

Jochen Ritzel
fuente
3
Depende de si desea evaluar los argumentos en el sitio de llamada de Perform o no.
Dave
6

(meses después) un pequeño ejemplo real donde lambda es útil, parcial no:
digamos que desea varias secciones transversales unidimensionales a través de una función bidimensional, como cortes en una fila de colinas.
quadf( x, f )toma un 1-d fy lo llama por varios x.
Para llamarlo para cortes verticales en y = -1 0 1 y cortes horizontales en x = -1 0 1,

fx1 = quadf( x, lambda x: f( x, 1 ))
fx0 = quadf( x, lambda x: f( x, 0 ))
fx_1 = quadf( x, lambda x: f( x, -1 ))
fxy = parabola( y, fx_1, fx0, fx1 )

f_1y = quadf( y, lambda y: f( -1, y ))
f0y = quadf( y, lambda y: f( 0, y ))
f1y = quadf( y, lambda y: f( 1, y ))
fyx = parabola( x, f_1y, f0y, f1y )

Que yo sepa, partialno puedo hacer esto.

quadf( y, partial( f, x=1 ))
TypeError: f() got multiple values for keyword argument 'x'

(¿Cómo agregar etiquetas numpy, parcial, lambda a esto?)

denis
fuente
5

Esto se llama funciones parciales y hay al menos 3 formas de hacerlo. Mi forma favorita es usar lambda porque evita la dependencia de un paquete adicional y es el menos detallado. Suponga que tiene una función add(x, y)y desea pasar add(3, y)a otra función como parámetro de manera que la otra función decida el valor y.

Utilizar lambda

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = lambda y: add(3, y)
    result = runOp(f, 1) # is 4

Crea tu propio envoltorio

Aquí debe crear una función que devuelva la función parcial. Obviamente, esto es mucho más detallado.

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# declare partial function
def addPartial(x):
    def _wrapper(y):
        return add(x, y)
    return _wrapper

# run example
def main():
    f = addPartial(3)
    result = runOp(f, 1) # is 4

Usar parcial de functools

Esto es casi idéntico al que se lambdamuestra arriba. Entonces, ¿por qué necesitamos esto? Hay algunas razones . En resumen, partialpuede ser un poco más rápido en algunos casos (vea su implementación ) y que puede usarlo para la unión temprana vs la unión tardía de lambda.

from functools import partial

# generic function takes op and its argument
def runOp(op, val):
    return op(val)

# declare full function
def add(x, y):
    return x+y

# run example
def main():
    f = partial(add, 3)
    result = runOp(f, 1) # is 4
Shital Shah
fuente
1

Aquí hay una manera de hacerlo con un cierre:

    def generate_add_mult_func(func):
        def function_generator(x):
            return reduce(func,range(1,x))
        return function_generator

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

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

    adding=generate_add_mult_func(add)
    multiplying=generate_add_mult_func(mult)

    print adding(10)
    print multiplying(10)
Stefan Gruenwald
fuente
En cualquier caso, uno debe hacer más que simplemente pasar una función a otra, el cierre es el camino a seguir.
jake77