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

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 )

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

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

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


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


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

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)

En cualquier caso, uno debe hacer más que simplemente pasar una función a otra, el cierre es el camino a seguir.
jake77
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.