设置每个实例 __call__ 的方法?

Dav*_*ave 5 python

我想做的是这样的:

class Foo(object):
    def __init__(self):
        pass
    def f(self):
        print "f"
    def g(self):
        print "g"


# programatically set the "default" operation
fer=Foo()
fer.__call__=fer.f

# a different instance does something else as its
# default operation
ger=Foo()
ger.__call__=ger.g

fer()  # invoke different functions on different
ger()  # objects depending on how they were set up.
Run Code Online (Sandbox Code Playgroud)

但从 2.7(我目前正在使用)开始,我无法执行此操作,尝试fer() 引发异常。

实际上,有没有办法设置每个实例的__call__方法?

mgi*_*son 3

不幸的是,普通的东西types.MethodType在这里不起作用,因为__call__它是一种特殊的方法。

从数据模型来看:

仅当类具有__call__()方法时,类实例才可调用;x(arguments) 是 的简写x.__call__(arguments)

这对于实际调用的内容有点含糊,但很明显您的类需要有一个__call__方法。

你需要创建某种黑客:

class Foo(object):
    def __init__(self):
        pass
    def f(self):
        print "f"
    def g(self):
        print "g"

    def __call__(self):
        return self.__call__()

f = Foo()
f.__call__ = f.f
f()

g = Foo()
g.__call__ = g.g
g()
Run Code Online (Sandbox Code Playgroud)

__call__不过要小心,如果在尝试调用实例之前没有在实例上设置 a ,则会导致无限递归。

请注意,我实际上并不建议调用您重新绑定的 magic 属性__call__。这里的重点是演示 python 翻译f()为: f.__class__.__call__(f),因此您无法在每个实例的基础上更改它。无论你做什么,类__call__都会被调用——你只需要做一些事情来改变类的__call__每个实例的行为,这很容易实现。


您可以使用 setter 类型的东西在您的类上实际创建方法(而不是简单的函数)——当然,这可以转换为属性:

import types
class Foo(object):
    def __init__(self):
        pass
    def f(self):
        print "f"
    def g(self):
        print "g"

    def set_func(self,f):
        self.func = types.MethodType(f,self)

    def __call__(self,*args,**kwargs):
        self.func(*args,**kwargs)

f = Foo()
f.set_func(Foo.f)
f()

def another_func(self,*args):
    print args

f.set_func(another_func)
f(1,2,3,"bar")
Run Code Online (Sandbox Code Playgroud)