Passando funções com argumentos para outra função no Python?

204

É possível passar funções com argumentos para outra função no Python?

Diga algo como:

def perform(function):
    return function()

Mas as funções a serem passadas terão argumentos como:

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

Respostas:

290

Você quer dizer isso?

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
fonte
10
E os parâmetros nomeados? Ou seja, def action1(arg1, arg2=None, arg3=None)como você poderia passar um argumento que pretende atribuir ao arg3, por exemplo?
precisa saber é o seguinte
6
executar (divertido, ** args), consulte stackoverflow.com/questions/8954746/…
Mannaggia
E se performe action1, action2em diferentes arquivos? @ S.Lott
alper
@alper importá- los
pfabri
122

É para isso que serve o lambda:

def Perform(f):
    f()

Perform(lambda: Action1())
Perform(lambda: Action2(p))
Perform(lambda: Action3(p, r))
Dave
fonte
7
Também por curiosidade, você pode me dizer por que as lambdas não são boas para este caso?
31820 Joan Venge
11
lambdas são uma das melhores características de boas linguagens de programação. infelizmente, a implementação do Python é severamente limitada. Neste caso, no entanto, eles se encaixam perfeitamente
Javier
3
Acho que a sintaxe limitada é quase opaca; eles são difíceis de explicar para o n00bz. Sim, eles funcionam aqui, e os recursos confusos da sintaxe estão ausentes. Este é - talvez - o único exemplo que eu já vi de uma lambda que não é obscura.
S.Lott
11
Para que você possa recuperar o resultado da função passada, não seria melhor se Perform () chamasse "return f ()" em vez de apenas chamar f ().
Mhawke 30/04/09
Eu acho que a versão lambda é bastante elegante, mas estranhamente nos testes que eu executei, era mais lento chamar funções via lambda do que pelo método fn (* args) discutido em outra resposta.
Richard Shepherd
39

Você pode usar a função parcial de functools dessa maneira.

from functools import partial

def perform(f):
    f()

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

Também funciona com palavras-chave

perform(partial(Action4, param1=p))
nulo
fonte
1
functools.partialtambém é mais versátil se performprecisar entregar outros parâmetros para f. Por exemplo, alguém poderia ligar perform(partial(Action3, p))e perform(f)fazer algo assim f("this is parameter r").
Robert
13

Use functools.partial, não lambdas! E ofc Perform é uma função inútil, você pode repassar funções diretamente.

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

Jochen Ritzel
fonte
3
Depende se você deseja que os argumentos sejam avaliados no site de chamada do Perform ou não.
29409 Dave
6

(meses depois), um pequeno exemplo real em que o lambda é útil, não parcial:
digamos que você queira várias seções transversais unidimensionais por meio de uma função bidimensional, como fatias por uma fileira de colinas.
quadf( x, f )tira 1-d fe chama-o para vários x.
Para chamá-lo para cortes verticais em y = -1 0 1 e cortes horizontais em 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 )

Até onde eu sei, partialnão posso fazer isso -

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

(Como adicionar tags numpy, parcial, lambda a isso?)

denis
fonte
5

Isso é chamado de funções parciais e há pelo menos três maneiras de fazer isso. Minha maneira favorita é usar o lambda porque evita a dependência de pacotes extras e é o menos detalhado. Suponha que você tenha uma função add(x, y)e deseja passar add(3, y)para outra função como parâmetro, de forma que a outra função decida o valor para y.

Use 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

Crie seu próprio invólucro

Aqui você precisa criar uma função que retorne a função parcial. Isto é obviamente muito mais detalhado.

# 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

Use parcial de functools

Isso é quase idêntico ao lambdamostrado acima. Então, por que precisamos disso? Existem algumas razões . Em resumo, partialpode ser um pouco mais rápido em alguns casos (consulte sua implementação ) e que você pode usá-lo para ligação antecipada versus ligação tardia do 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
fonte
1

Aqui está uma maneira de fazer isso com um fechamento:

    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
fonte
De qualquer forma, é preciso fazer mais do que apenas passar uma função para outro fechamento.
jake77