__call__是唯一让Python类实例变为可调用对象的方式,因其直接被解释器识别并触发callable(obj)为True,其他方法如继承Callable或重写__getattr__均无效。

为什么 __call__ 是唯一靠谱的方式
Python 类要变成可调用对象,核心就是实现 __call__ 魔术方法。没有替代方案——继承 collections.abc.Callable 不会自动让它可调用,重写 __getattr__ 或加装饰器也绕不开这个方法。它本质是让实例像函数一样被 () 调用,解释器在遇到 obj() 时,底层就查 obj.__call__ 并执行。
常见错误是以为只要类里有 def run(self): 就能写 obj.run() 算“可调用”,但这只是普通方法调用,不是对象本身的可调用性。真正的可调用性体现在 callable(obj) 返回 True,而这个判断依据就是 hasattr(obj, '__call__')。
怎么写一个带状态的可调用类
这是 __call__ 最典型的价值场景:把函数逻辑和内部状态(比如计数器、缓存、配置)绑在一起,比闭包更清晰,比全局变量更安全。
- 必须定义为实例方法,第一个参数是
self,后续参数按需添加 - 可以在
__init__中初始化状态,在__call__中读写或更新 - 返回值任意,不强制
return;无返回值时默认返回None
class Counter:
def __init__(self, start=0):
self.count = start
<pre class="brush:php;toolbar:false;">def __call__(self, step=1):
self.count += step
return self.countinc = Counter(10) print(inc()) # 11 print(inc(5)) # 16 print(callable(inc)) # True
__call__ 和普通方法在性能与用途上的区别
从字节码角度看,obj() 和 obj.method() 都是属性查找 + 调用,但 __call__ 多一层协议检查(是否实现),实际开销微乎其微。关键差异在语义和使用方式:
- 函数式编程中,可调用对象能直接传给
map、filter、functools.partial等高阶函数,无需额外包装成 lambda - 作为回调时,比如
threading.Timer(1.0, obj),要求目标是 callable,此时obj必须有__call__ - 不能靠
__call__实现多态调度(比如根据参数类型分发),那该用多重分派或functools.singledispatchmethod
容易踩的坑:参数绑定、继承与装饰器干扰
很多问题不是出在 __call__ 本身,而是周边上下文没理清:
- 如果类同时被
@dataclass或@cached_property装饰,确保这些装饰器不覆盖或干扰__call__的定义顺序 - 子类继承父类的
__call__时,若想扩展逻辑,必须显式调用super().__call__(...),否则会被完全覆盖 - 用
functools.wraps包装__call__没有意义——wraps是为函数设计的,对实例方法无效;想保留元信息,得手动复制__doc__、__name__等
最常被忽略的一点:可调用对象的状态是实例级的,不是类级的。多个实例之间互不影响,这点和模块级函数+闭包变量不同,别误以为改了一个实例的 self.x 会影响其他实例。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











