4

我想做的是:

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__方法?

4

3 回答 3

3

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

从数据模型:

类实例只有在类有__call__()方法时才可调用;x(arguments) 是 的简写x.__call__(arguments)

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

您需要创建某种 hack:

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__不过要小心,如果在尝试调用它之前没有在实例上设置 a ,它将导致无限递归。

请注意,我实际上不建议调用您 rebind 的魔法属性__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")
于 2013-01-29T16:03:46.767 回答
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

我真的很喜欢第二种方式,因为它提供了一个干净的类层次结构并且对我来说看起来很干净。

于 2013-01-29T16:15:29.927 回答
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)
于 2013-01-29T16:01:23.013 回答