Comment passer une méthode en tant que paramètre en Python

204

Est-il possible de passer une méthode comme paramètre à une méthode?

self.method2(self.method1)

def method1(self):
    return 'hello world'

def method2(self, methodToRun):
    result = methodToRun.call()
    return result
Dan
la source

Réponses:

277

Oui, il suffit d'utiliser le nom de la méthode, comme vous l'avez écrit. Les méthodes et les fonctions sont des objets en Python, comme toute autre chose, et vous pouvez les transmettre comme vous le faites pour les variables. En fait, vous pouvez considérer une méthode (ou une fonction) comme une variable dont la valeur est l'objet de code appelable réel.

Puisque vous avez posé des questions sur les méthodes, j'utilise des méthodes dans les exemples suivants, mais notez que tout ce qui suit s'applique de manière identique aux fonctions (sauf sans le selfparamètre).

Pour appeler une méthode ou une fonction passée, vous utilisez simplement le nom auquel elle est liée de la même manière que vous utiliseriez le nom régulier de la méthode (ou de la fonction):

def method1(self):
    return 'hello world'

def method2(self, methodToRun):
    result = methodToRun()
    return result

obj.method2(obj.method1)

Remarque: je crois qu'une __call__()méthode existe, c'est-à-dire que vous pourriez techniquement le faire methodToRun.__call__(), mais vous ne devriez probablement jamais le faire explicitement. __call__()est destiné à être implémenté, pas à être appelé à partir de votre propre code.

Si vous vouliez method1être appelé avec des arguments, les choses se compliquent un peu. method2doit être écrit avec un peu d'informations sur la façon de passer des arguments à method1, et il doit obtenir des valeurs pour ces arguments de quelque part. Par exemple, si method1est censé prendre un argument:

def method1(self, spam):
    return 'hello ' + str(spam)

alors vous pouvez écrire method2pour l'appeler avec un argument qui est passé:

def method2(self, methodToRun, spam_value):
    return methodToRun(spam_value)

ou avec un argument qu'il calcule lui-même:

def method2(self, methodToRun):
    spam_value = compute_some_value()
    return methodToRun(spam_value)

Vous pouvez étendre cela à d'autres combinaisons de valeurs transmises et de valeurs calculées, comme

def method1(self, spam, ham):
    return 'hello ' + str(spam) + ' and ' + str(ham)

def method2(self, methodToRun, ham_value):
    spam_value = compute_some_value()
    return methodToRun(spam_value, ham_value)

ou même avec des arguments de mots clés

def method2(self, methodToRun, ham_value):
    spam_value = compute_some_value()
    return methodToRun(spam_value, ham=ham_value)

Si vous ne savez pas, lors de l'écriture method2, quels arguments methodToRunvont prendre, vous pouvez également utiliser la décompression d'arguments pour l'appeler de manière générique:

def method1(self, spam, ham):
    return 'hello ' + str(spam) + ' and ' + str(ham)

def method2(self, methodToRun, positional_arguments, keyword_arguments):
    return methodToRun(*positional_arguments, **keyword_arguments)

obj.method2(obj.method1, ['spam'], {'ham': 'ham'})

Dans ce cas, il positional_argumentsdoit s'agir d'une liste ou d'un tuple ou similaire, et keyword_argumentsest un dict ou similaire. Dans method2vous pouvez modifier positional_argumentset keyword_arguments(par exemple pour ajouter ou supprimer certains arguments ou changer les valeurs) avant d'appeler method1.

David Z
la source
Je suis confus par votre apparente confusion du mot methodavec function. def method1(spam):est une définition de fonction, pas une définition de méthode.
Michael Dorst
@MichaelDorst Oui, pour autant que je me souvienne, j'utilisais «méthode» pour être cohérent avec la terminologie utilisée dans la question, même si ce sont techniquement des fonctions. Je pensais que la différence entre les méthodes et les fonctions serait une distraction par rapport au point principal que j'essayais de faire valoir. Bien que je suppose que je pourrais clarifier cela avec une modification ... Je ne suis pas sûr que cela en vaille la peine.
David Z
Vous avez répondu à une question différente de celle qui vous a été posée. OP a demandé si vous pouviez passer une méthode comme paramètre, par exemple func(obj.method). Vous avez expliqué comment passer une fonction en tant que paramètre par exemple func(other_func), mais en vous référant aux fonctions en tant que méthodes. J'ai trouvé cela très déroutant.
Michael Dorst
@MichaelDorst Oh, je vois ce que tu veux dire. Je ne savais pas que quelqu'un serait tellement accroché à la distinction méthode / fonction. Permettez-moi de faire une clarification.
David Z
35

Oui c'est possible. Appelez-le simplement:

class Foo(object):
    def method1(self):
        pass
    def method2(self, method):
        return method()

foo = Foo()
foo.method2(foo.method1)

la source
1
et s'il n'y a pas d'instance foo?
Lei Yang
1
Ensuite, vous n'avez tout simplement pas besoin de foo, par exemple: def method1(): pass def method2(method) return method() method2(method1)
Tom
16

Voici votre exemple réécrit pour montrer un exemple de travail autonome:

class Test:
    def method1(self):
        return 'hello world'

    def method2(self, methodToRun):
        result = methodToRun()
        return result

    def method3(self):
        return self.method2(self.method1)

test = Test()

print test.method3()
Trent
la source
7

Oui; les fonctions (et méthodes) sont des objets de première classe en Python. Les travaux suivants:

def foo(f):
    print "Running parameter f()."
    f()

def bar():
    print "In bar()."

foo(bar)

Les sorties:

Running parameter f().
In bar().

Il est facile de répondre à ces types de questions en utilisant l'interpréteur Python ou, pour plus de fonctionnalités, le shell IPython .

lt_kije
la source
7

Si vous voulez passer une méthode d'une classe comme argument mais que vous n'avez pas encore l'objet sur lequel vous allez l'appeler, vous pouvez simplement passer l'objet une fois que vous l'avez comme premier argument (ie le "self" argument).

class FooBar:

    def __init__(self, prefix):
        self.prefix = prefix

    def foo(self, name):
        print "%s %s" % (self.prefix, name)


def bar(some_method):
    foobar = FooBar("Hello")
    some_method(foobar, "World")

bar(FooBar.foo)

Cela imprimera "Hello World"

Face avant
la source
7

Beaucoup de bonnes réponses mais étranges que personne n'ait mentionnées en utilisant une lambdafonction.
Donc, si vous n'avez pas d'arguments, les choses deviennent assez triviales:

def method1():
    return 'hello world'

def method2(methodToRun):
    result = methodToRun()
    return result

method2(method1)

Mais disons que vous avez un (ou plusieurs) arguments dans method1:

def method1(param):
    return 'hello ' + str(param)

def method2(methodToRun):
    result = methodToRun()
    return result

Ensuite, vous pouvez simplement invoquer method2comme method2(lambda: method1('world')).

method2(lambda: method1('world'))
>>> hello world
method2(lambda: method1('reader'))
>>> hello reader

Je trouve cela beaucoup plus propre que les autres réponses mentionnées ici.

Scrotch
la source
S'il s'agissait d'une valeur dans un dictionnaire, comment pourrais-je l'exécuter plutôt que de renvoyer un objet fonction? Edit: Je viens de réaliser que je peux simplement mettre ()à la fin de l'objet dans mon appel de retour, duh.
vaponteblizzard le
4

Les méthodes sont des objets comme les autres. Vous pouvez donc les faire circuler, les stocker dans des listes et des dictionnaires, en faire ce que vous voulez. La particularité d'eux est qu'ils sont des objets appelables, vous pouvez donc les invoquer __call__. __call__est appelé automatiquement lorsque vous appelez la méthode avec ou sans arguments, il vous suffit donc d'écrire methodToRun().

Vasil
la source
1

Pas exactement ce que vous voulez, mais un outil utile connexe est getattr()d'utiliser le nom de la méthode comme paramètre.

class MyClass:
   def __init__(self):
      pass
   def MyMethod(self):
      print("Method ran")

# Create an object
object = MyClass()
# Get all the methods of a class
method_list = [func for func in dir(MyClass) if callable(getattr(MyClass, func))]
# You can use any of the methods in method_list
# "MyMethod" is the one we want to use right now

# This is the same as running "object.MyMethod()"
getattr(object,'MyMethod')()
Dersu Giritlioğlu
la source