在Python中使对象可调用的问题

26 投票
3 回答
13175 浏览
提问于 2025-04-15 15:50

我写了这样的代码

>>> class a(object):
        def __init__(self):
            self.__call__ = lambda x:x

>>> b = a()

我原本以为类 a 的对象应该是可以被调用的,但显然它不是,我搞不懂为什么。

>>> b()

Traceback (most recent call last):
    File "<pyshell#5>", line 1, in <module>
        b()
TypeError: 'a' object is not callable
>>> callable(b)
False

>>> hasattr(b,'__call__')
True

3 个回答

2

这个怎么样呢?

首先,定义一个基础类叫做 AllowDynamicCall:

class AllowDynamicCall(object):
     def __call__(self, *args, **kwargs):
         return self._callfunc(self, *args, **kwargs)

然后,创建一个继承自 AllowDynamicCall 的子类:

class Example(AllowDynamicCall):
     def __init__(self):
         self._callfunc = lambda s: s
20

__call__ 这个方法需要在类里面定义,而不是在实例里面。

class a(object):
    def __init__(self):
        pass

    def __call__(self):
        return self

(这里用 def 是因为 PEP8 不建议使用命名的 lambda 函数。)

如果你想让每个实例有不同的行为,可以这样做:

class a(object):
    def __init__(self):
        self.myfunc = lambda x:x

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

特殊方法是根据对象的类型(比如说类)来查找的,而不是根据具体的实例来查找。想想看,如果一个类定义了 __call__ 方法,那么当这个类被调用时,__call__ 方法应该被执行……那可真是个大麻烦!但幸运的是,特殊方法是根据类的类型,也就是元类来查找的,这样一切就正常了(“旧式类”在这方面表现得非常不规律,所以我们现在都用新式类,而在Python 3中只有新式类可用)。

所以,如果你需要对特殊方法进行“每个实例单独覆盖”,你必须确保每个实例都有自己独特的类。这其实很简单:

class a(object):
    def __init__(self):
        self.__class__ = type(self.__class__.__name__, (self.__class__,), {})
        self.__class__.__call__ = lambda x:x

这样就可以了。当然,在这个情况下这样做有点傻,因为每个实例最终都只有一个相同的“所谓每个实例的” __call__ 方法,但如果你真的需要针对每个单独的实例进行覆盖,这样做就很有用了。

撰写回答