Как передать метод в качестве параметра в Python

Можно ли передать метод в качестве параметра методу?

self.method2(self.method1)

def method1(self):
    return 'hello world'

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

person Dan    schedule 01.04.2009    source источник


Ответы (9)


Да, просто используйте название метода, как вы написали. В Python методы и функции являются объектами, как и все остальное, и вы можете передавать их так же, как и переменные. Фактически, вы можете думать о методе (или функции) как о переменной, значение которой является фактическим вызываемым объектом кода.

Поскольку вы спрашивали о методах, я использую методы в следующих примерах, но обратите внимание, что все, что ниже, одинаково применимо к функциям (за исключением параметра self).

Чтобы вызвать переданный метод или функцию, вы просто используете имя, к которому она привязана, так же, как вы использовали бы обычное имя метода (или функции):

def method1(self):
    return 'hello world'

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

obj.method2(obj.method1)

Примечание. Я считаю, что метод __call__() действительно существует, т.е. вы можете технически использовать methodToRun.__call__(), но вам, вероятно, никогда не следует делать это явно. __call__() предназначен для реализации, а не для вызова из вашего собственного кода.

Если вы хотите, чтобы method1 вызывался с аргументами, все становится немного сложнее. method2 должен быть написан с небольшой информацией о том, как передавать аргументы в method1, и он должен откуда-то получать значения для этих аргументов. Например, если method1 должен принимать один аргумент:

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

тогда вы можете написать method2, чтобы вызвать его с одним переданным аргументом:

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

или с аргументом, который вычисляет сам:

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

Вы можете расширить это до других комбинаций переданных значений и вычисленных значений, например

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)

или даже с аргументами ключевого слова

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

Если при написании method2 вы не знаете, какие аргументы будут принимать methodToRun, вы также можете использовать распаковку аргументов, чтобы вызвать его обычным способом:

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'})

В этом случае positional_arguments должен быть списком, кортежем или подобным, а keyword_arguments - dict или подобным. В method2 вы можете изменить positional_arguments и keyword_arguments (например, добавить или удалить определенные аргументы или изменить значения) перед вызовом method1.

person David Z    schedule 01.04.2009
comment
Меня смущает ваше очевидное смешение слова method с function. def method1(spam): - это определение функции, а не определение метода. - person Michael Dorst; 25.08.2020
comment
@MichaelDorst Да, насколько я помню, я использовал метод, чтобы соответствовать терминологии, использованной в вопросе, даже если это технически функции. Я думал, что разница между методами и функциями отвлечет от основной мысли, которую я пытался сформулировать. Хотя, полагаю, я мог прояснить это с помощью редактирования ... Я не уверен, что оно того стоит. - person David Z; 25.08.2020
comment
Вы ответили на другой вопрос, чем был задан. OP спросил, можете ли вы передать метод в качестве параметра, например. func(obj.method). Вы объяснили, как передать функцию в качестве параметра, например. func(other_func), но пока относится к функциям как к методам. Я нашел это очень запутанным. - person Michael Dorst; 25.08.2020
comment
@MichaelDorst О, я понимаю, о чем вы. Я не понимал, что кто-то так сильно зациклится на различии метода / функции. Позвольте мне внести уточняющую правку. - person David Z; 29.08.2020

Да, это возможно. Просто назовите это:

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

foo = Foo()
foo.method2(foo.method1)
person Community    schedule 01.04.2009
comment
что, если экземпляра foo нет? - person Lei Yang; 26.07.2017
comment
Тогда вам просто не понадобится foo, например: def method1(): pass def method2(method) return method() method2(method1) - person Tom; 16.10.2017

Вот ваш пример, переписанный, чтобы показать автономный рабочий пример:

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()
person Trent    schedule 01.04.2009

Да; функции (и методы) - это объекты первого класса в Python. Следующие работы:

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

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

foo(bar)

Выходы:

Running parameter f().
In bar().

На такие вопросы легко ответить, используя интерпретатор Python или, для получения дополнительных возможностей, оболочку IPython.

person lt_kije    schedule 01.04.2009

Если вы хотите передать метод класса в качестве аргумента, но еще не имеете объекта, для которого вы собираетесь его вызвать, вы можете просто передать объект, как только он будет у вас в качестве первого аргумента (т.е. аргумент).

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)

Это напечатает "Hello World".

person FrontSide    schedule 13.06.2018

Используйте функцию lambda.
Итак, если у вас нет аргументов, все становится довольно тривиальным:

def method1():
    return 'hello world'

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

method2(method1)

Но предположим, что у вас есть один (или несколько) аргументов в method1:

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

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

Затем вы можете просто вызвать method2 как method2(lambda: method1('world')).

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

Я считаю это намного чище, чем другие ответы, упомянутые здесь.

person Scrotch    schedule 12.06.2019
comment
Если бы это было значение в словаре, как бы я мог выполнить его, а не возвращать объект функции? Изменить: только что понял, что могу просто поставить () в конце объекта в моем обратном вызове, да. - person vaponteblizzard; 12.06.2020

Методы - это такие же объекты, как и любые другие. Так что вы можете передавать их, хранить в списках и диктовках, делать с ними все, что захотите. Их особенность заключается в том, что они являются вызываемыми объектами, поэтому вы можете вызывать для них __call__. __call__ вызывается автоматически, когда вы вызываете метод с аргументами или без них, поэтому вам просто нужно написать methodToRun().

person Vasil    schedule 01.04.2009

Не совсем то, что вам нужно, но связанный полезный инструмент - getattr(), чтобы использовать имя метода в качестве параметра.

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')()
person Dersu Giritlioğlu    schedule 16.04.2020

Пример: простая оболочка вызова функции:

def measure_cpu_time(f, *args):
    t_start = time.process_time()
    ret = f(*args)
    t_end = time.process_time()
    return t_end - t_start, ret
person Sean    schedule 11.11.2020