Passer des fonctions avec des arguments à une autre fonction en Python?


205

Est-il possible de passer des fonctions avec des arguments à une autre fonction en Python?

Dites quelque chose comme:

def perform(function):
    return function()

Mais les fonctions à passer auront des arguments comme:

action1()
action2(p)
action3(p,r)

Réponses:


291

Est-ce que tu veut dire ça?

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'en est-il des paramètres nommés? Autrement dit, def action1(arg1, arg2=None, arg3=None)comment pourriez-vous passer un argument que vous avez l'intention d'être affecté à arg3, par exemple?
ChaimKut

6
effectuer (amusant, ** arguments), voir stackoverflow.com/questions/8954746/…
Mannaggia

Que faire si performet action1, action2sur différents fichiers? @ S.Lott
alper

@alper importer les
pfabri

122

Voici à quoi sert lambda:

def Perform(f):
    f()

Perform(lambda: Action1())
Perform(lambda: Action2(p))
Perform(lambda: Action3(p, r))

7
Aussi par curiosité, pouvez-vous s'il vous plaît me dire pourquoi les lambdas ne sont pas bons pour ce cas?
Joan Venge

11
les lambdas sont l'une des meilleures fonctionnalités des bons langages de programmation. malheureusement, l'implémentation de Python est sévèrement limitée. dans ce cas, cependant, ils correspondent parfaitement
Javier

3
Je trouve que la syntaxe limitée est presque opaque; ils sont difficiles à expliquer à n00bz. Oui, ils fonctionnent ici et les caractéristiques déroutantes de la syntaxe sont absentes. C'est - peut-être - le seul exemple que j'ai vu d'une lambda qui n'est pas obscure.
S.Lott

11
Pour que vous puissiez récupérer le résultat de la fonction passée, ne serait-il pas préférable que Perform () appelle "return f ()" plutôt que d'appeler simplement f ().
mhawke

Je pense que la version lambda est assez soignée, mais bizarrement dans les tests que j'ai exécutés, il était plus lent d'appeler des fonctions via le lambda que par la méthode fn (* args) discutée dans une autre réponse.
Richard Shepherd

39

Vous pouvez utiliser la fonction partielle de functools comme ça.

from functools import partial

def perform(f):
    f()

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

Fonctionne également avec les mots clés

perform(partial(Action4, param1=p))

1
functools.partialest également plus polyvalent si vous avez performbesoin de transmettre d'autres paramètres à f. Par exemple, on pourrait appeler perform(partial(Action3, p))et perform(f)faire quelque chose comme f("this is parameter r").
Robert

13

Utilisez functools.partial, pas lambdas! Et ofc Perform est une fonction inutile, vous pouvez passer directement les fonctions.

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


3
Cela dépend si vous souhaitez que les arguments soient évalués sur le site d'appel de Perform ou non.
Dave

6

(des mois plus tard) un petit exemple réel où lambda est utile, non partiel:
disons que vous voulez diverses coupes transversales à 1 dimension à travers une fonction à 2 dimensions, comme des tranches à travers une rangée de collines.
quadf( x, f )prend un 1-d fet l'appelle pour divers x.
Pour l'appeler pour les coupes verticales à y = -1 0 1 et les coupes horizontales à 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 )

Pour autant que je sache, je ne partialpeux pas faire ça -

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

(Comment ajouter des balises numpy, partial, lambda à cela?)


5

C'est ce qu'on appelle des fonctions partielles et il y a au moins 3 façons de le faire. Ma façon préférée utilise lambda car elle évite la dépendance à l'égard d'un paquet supplémentaire et est la moins verbeuse. Supposons que vous ayez une fonction add(x, y)et que vous souhaitiez passer add(3, y)à une autre fonction en tant que paramètre de sorte que l'autre fonction décide de la valeur pour y.

Utilisez 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

Créez votre propre emballage

Ici, vous devez créer une fonction qui renvoie la fonction partielle. C'est évidemment beaucoup plus verbeux.

# 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

Utiliser partiel à partir de functools

Ceci est presque identique à celui lambdamontré ci-dessus. Alors pourquoi en avons-nous besoin? Il y a peu de raisons . En bref, partialpeut être un peu plus rapide dans certains cas (voir son implémentation ) et que vous pouvez l'utiliser pour une liaison anticipée vs une liaison tardive 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

Voici une façon de le faire avec une fermeture:

    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)

Dans tous les cas, il faut faire plus que simplement passer une fonction à une autre fermeture est la voie à suivre.
jake77
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.