Como passo um método como parâmetro no Python

191

É possível passar um método como parâmetro para um método?

self.method2(self.method1)

def method1(self):
    return 'hello world'

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

Respostas:

263

Sim, é só usar o nome do método, como você escreveu. Métodos / funções são objetos em Python, como qualquer outra coisa, e você pode transmiti-los da mesma maneira que faz variáveis. De fato, você pode pensar em um método (ou função) como uma variável cujo valor é o objeto de código realizável.

Para sua informação, não há callmétodo - acho que é chamado __call__, mas você não precisa invocá-lo explicitamente:

def method1():
    return 'hello world'

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

method2(method1)

Se você quiser method1ser chamado com argumentos, as coisas ficam um pouco mais complicadas. method2precisa ser escrito com um pouco de informação sobre como passar argumentos method1e precisa obter valores para esses argumentos de algum lugar. Por exemplo, se você method1deve receber um argumento:

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

então você pode escrever method2para chamá-lo com um argumento que é passado:

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

ou com um argumento que se calcula:

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

Você pode expandir isso para outras combinações de valores passados ​​e valores calculados, como

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

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

ou mesmo com argumentos de palavra-chave

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

Se você não souber, ao escrever method2, quais methodToRunargumentos serão utilizados, também poderá usar a descompactação de argumentos para chamá-lo de maneira genérica:

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

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

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

Nesse caso, positional_argumentsprecisa ser uma lista ou tupla ou similar e keyword_argumentsé um ditado ou semelhante. Em method2que você pode modificar positional_argumentse keyword_arguments(por exemplo, para adicionar ou remover certos argumentos ou alterar os valores) antes de chamar method1.

David Z
fonte
34

Sim, é possível. Basta chamá-lo:

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

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

fonte
1
e se não houver instância foo?
Lei Yang
1
Então você simplesmente não precisa foo, por exemplo: def method1(): pass def method2(method) return method() method2(method1)
Tom
14

Aqui está seu exemplo reescrito para mostrar um exemplo de trabalho independente:

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
fonte
6

Sim; funções (e métodos) são objetos de primeira classe em Python. Os seguintes trabalhos:

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

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

foo(bar)

Saídas:

Running parameter f().
In bar().

É fácil responder a esse tipo de perguntas usando o interpretador Python ou, para mais recursos, o shell IPython .

lt_kije
fonte
5

Se você deseja passar o método de uma classe como argumento, mas ainda não possui o objeto para o qual irá chamá-lo, basta passar o objeto assim que tiver o primeiro argumento (ou seja, o "eu") argumento).

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)

Isso imprimirá "Olá Mundo"

FrontSide
fonte
5

Muitas respostas boas, mas estranhas que ninguém mencionou usando uma lambdafunção.
Portanto, se você não tem argumentos, as coisas se tornam bastante triviais:

def method1():
    return 'hello world'

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

method2(method1)

Mas digamos que você tenha um (ou mais) argumentos em method1:

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

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

Então você pode simplesmente chamar method2como method2(lambda: method1('world')).

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

Acho isso muito mais limpo do que as outras respostas mencionadas aqui.

Scrotch
fonte
Se isso fosse um valor em um dicionário, como eu poderia executá-lo em vez de retornar um objeto de função? Edit: Acabei de perceber que posso colocar ()no final do objeto na minha chamada de retorno, duh.
vaponteblizzard 11/06
3

Métodos são objetos como qualquer outro. Assim, você pode distribuí-los, armazená-los em listas e ditados, fazer o que quiser com eles. A coisa especial sobre eles é que eles são objetos que podem ser chamados para que você possa invocá __call__-los. __call__é chamado automaticamente quando você invoca o método com ou sem argumentos, então você só precisa escrever methodToRun().

Vasil
fonte
0

Não é exatamente o que você deseja, mas uma ferramenta útil relacionada é getattr()usar o nome do método como parâmetro.

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
fonte