为何用@property装饰__call__后,Python实例调用与类直接调用行为矛盾?
a()能正常调用但type(a).__call__(a)报错? 先看这段代码:
class A: @property def __call__(self): def inner(): return 'Called.' return inner a = A() a() # 返回 'Called.' type(a).__call__(a) # 报错:'property' object is not callable.
根据Python官方文档描述:
object.__call__(self[, args...])Called when the instance is “called”
as a function; if this method is defined,x(arg1, arg2, ...)roughly
translates totype(x).__call__(x, arg1, ...).
实际运行结果和文档描述看似矛盾,背后的核心原因是双下划线方法的查找逻辑和普通属性/方法存在差异,具体拆解为两点:
执行
a()时,Python会优先从实例a的属性集合中查找__call__。由于你给__call__加了@property装饰器,访问a.__call__会触发property的getter方法,返回内部定义的inner可调用函数,所以a()本质等价于(a.__call__)(),自然能正常执行并返回结果。调用
type(a).__call__(a)时,你直接访问的是类A中定义的__call__属性——也就是那个property对象本身,而非经过实例触发getter后得到的可调用函数。这个property对象本身不具备可调用性,因此会抛出“'property' object is not callable”的错误。
官方文档里的等价描述,是针对常规类级别的__call__方法定义(即未用@property装饰的情况)。当你用@property把__call__改造成实例属性的动态getter时,就绕过了类级方法的常规调用逻辑,从而出现了看似矛盾的现象。
内容的提问来源于stack exchange,提问作者Zhang Daniel

