如何在Python中将方法作为参数传递

259

能否将方法作为参数传递给另一个方法?

self.method2(self.method1)

def method1(self):
    return 'hello world'

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

正如这里的答案所指出的那样,使用方法与使用普通函数并没有什么不同;我将其关闭为最佳规范的重复项,其中我已经添加了自己的答案并给出了使用方法的示例。 - Karl Knechtel
9个回答

335
是的,可以使用方法的名称,就像你已经写的那样。在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则需要是一个字典或类似的结构。在method2中,您可以在调用method1之前修改positional_argumentskeyword_arguments(例如添加或删除某些参数或更改值)。

2
我对你混淆methodfunction这两个词感到困惑。def method1(spam):是函数定义,而不是方法定义。 - Michael Dorst
1
@MichaelDorst 是的,就我所记得的而言,我使用“方法”是为了与问题中使用的术语保持一致,尽管这些技术上是函数。我认为方法和函数之间的区别会分散我试图传达的主要观点。虽然我想我可以通过编辑来澄清这一点...不过我不确定是否值得这样做。 - David Z
2
你回答的问题与被问的不同。OP问的是是否可以将一个方法作为参数传递,例如 func(obj.method)。而你解释了如何将函数作为参数传递,例如 func(other_func),但是在引用函数时却称其为方法。我觉得这很令人困惑。 - Michael Dorst
@MichaelDorst 哦,我明白你的意思了。我没有意识到有人会在方法/函数区别上卡住那么久。让我做一个澄清的编辑。 - David Z

41

是的,这是可能的。只需调用它:

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

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

4
如果没有名为“foo”的实例会怎样? - Lei Yang
2
那么你就不需要foo,例如: `def method1():
pass
def method2(method)
return method() method2(method1)`
- Tom

16

以下是您的示例重写后的独立工作示例:

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()

15

使用 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

我觉得这个比其他回答提到的更干净。


如果这是字典中的一个值,我该如何执行它而不是返回一个函数对象?编辑:刚意识到我可以在返回调用中的对象末尾加上 (),呃。 - vaponteblizzard
你可以通过值或引用传递参数,但是在函数的情况下,LAMBDA绝对是处理函数参数的最佳方式。 - undefined

11

如果你想将一个类的方法作为参数传递,但是目前还没有要调用该方法的对象,你可以在得到该对象后,将其作为第一个参数(即"self"参数)简单地传递。

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”


8
是的,Python中的函数(和方法)是一等对象。以下代码可以正常运行:
def foo(f):
    print "Running parameter f()."
    f()

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

foo(bar)

输出:

Running parameter f().
In bar().

使用Python解释器或更多功能的IPython shell,这些问题都很容易回答。


4

方法和其他对象一样。因此,您可以将它们传递,存储在列表和字典中,以任何方式处理它们。 它们的特别之处在于它们是可调用的对象,因此您可以在其上调用 __call__。当您带或不带参数调用该方法时,__call__ 会自动被调用,所以您只需要编写 methodToRun()


4

虽然不是您想要的,但一个相关的有用工具是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')()

3

例子:一个简单的函数调用包装器:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接