设置每个实例的__call__方法的方法是什么?
我想做的事情大概是这样的:
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.
但是在我现在使用的2.7版本中,我无法做到这一点,尝试使用fer()
时会出现错误。
有没有办法实际上为每个实例设置一个__call__
方法呢?
3 个回答
1
可能的解决方案:
class Foo(object):
def __init__(self):
self._callable = lambda s: None
def f(self):
print "f"
def set_callable(self, func):
self._callable = func
def g(self):
print "g"
def __call__(self):
return self._callable()
d = Foo()
d.set_callable(d.g)
2
你可能在解决一个错误的问题。
因为Python允许以过程的方式创建类,所以你可以写出这样的代码:
>>> def create_class(cb):
... class Foo(object):
... __call__ = cb
... return Foo
...
>>> Foo1 = create_class(lambda self: 42)
>>> foo1 = Foo1()
>>> foo1()
>>> Foo2 = create_class(lambda self: self.__class__.__name__)
>>> foo2 = Foo2()
>>> foo2()
不过请注意,在这种情况下,Foo1和Foo2没有共同的基类。所以 isinstance
和 issubclass
这两个函数是无法使用的。如果你希望它们有一个共同的基类,我建议使用以下代码:
>>> class Foo(object):
... @classmethod
... def create_subclass(cls, cb):
... class SubFoo(cls):
... __call__ = cb
... return SubFoo
...
>>> Foo1 = Foo.create_subclass(lambda self: 42)
>>> foo1 = Foo1()
>>> foo1()
>>> Foo2 = Foo.create_subclass(lambda self: self.__class__.__name__)
>>> foo1 = Foo2()
>>> foo2()
'Foo'
>>> issubclass(Foo1, Foo)
True
>>> issubclass(Foo2, Foo)
True
我个人非常喜欢第二种方法,因为它提供了一个清晰的类层次结构,看起来也很整洁。
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()
不过要小心,如果你在尝试调用实例之前没有给它设置 __call__
,就会导致无限递归。
值得注意的是,我并不推荐直接调用你重新绑定的魔法属性 __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")