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)
Joan Venge
la source

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 )
S.Lott
la source
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))
Dave
la source
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))
nul
la source
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()

Jochen Ritzel
la source
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?)

denis
la source
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
Shital Shah
la source
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)
Stefan Gruenwald
la source
Dans tous les cas, il faut faire plus que simplement passer une fonction à une autre fermeture est la voie à suivre.
jake77