Python __call__ 方法
最后修改于 2025 年 4 月 8 日
本综合指南探讨了 Python 的 __call__ 方法,该方法使实例可以像函数一样被调用。我们将介绍基本用法、有状态函数、装饰器和实际示例。
基本定义
__call__ 方法使实例可以像函数一样被调用。定义后,你可以使用带有括号和参数的实例。
主要特点:它使对象表现得像函数一样,在调用之间保持状态,并且对于创建仿函数(函数对象)至关重要。 该方法接收实例作为 self 以及任何调用参数。
基本 __call__ 实现
这是一个简单的类,实现了 __call__ 来演示基本功能。 该实例变得像函数一样可调用。
class Greeter:
def __init__(self, greeting):
self.greeting = greeting
def __call__(self, name):
return f"{self.greeting}, {name}!"
hello = Greeter("Hello")
print(hello("Alice")) # "Hello, Alice!"
print(hello("Bob")) # "Hello, Bob!"
此示例展示了 __call__ 如何使实例表现得像函数一样。 Greeter 实例在调用之间记住其 greeting 状态。
hello 对象可以使用括号和参数进行调用,就像常规函数一样,但会维护自己的状态。
有状态函数对象
__call__ 非常适合创建有状态函数对象,该对象可以记住调用之间的信息,这与常规函数不同。
class Counter:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
counter = Counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
此 Counter 类在调用之间维护状态。 每次调用都会递增计数并返回新值,从而演示有状态行为。
与基于闭包的解决方案不同,此方法提供了一种干净的面向对象的方式来维护函数调用之间的状态,并具有完整的类功能。
使用 __call__ 创建装饰器
基于类的装饰器通常使用 __call__ 来实现装饰器逻辑,同时在被装饰的函数调用之间维护状态。
class Trace:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"Call {self.calls} to {self.func.__name__}")
return self.func(*args, **kwargs)
@Trace
def square(x):
return x * x
print(square(5)) # Prints trace info and returns 25
print(square(3)) # Prints trace info and returns 9
此 Trace 装饰器记录对已装饰函数的每次调用。 __call__ 方法处理实际的函数调用。
装饰器在对已装饰函数的多次调用中维护状态(调用计数),从而演示了基于类的装饰器的关键优势。
使用 __call__ 进行记忆化
__call__ 方法可以通过缓存函数结果来实现记忆化,从而避免对相同输入进行重复计算。
class Memoize:
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args):
if args not in self.cache:
self.cache[args] = self.func(*args)
return self.cache[args]
@Memoize
def fibonacci(n):
if n in (0, 1):
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 55 (cached results speed up calculation)
此记忆化装饰器缓存斐波那契数列结果,以显着提高性能。 缓存保存在实例字典中。
每次调用都会先检查缓存,然后再进行计算,并存储新结果以供将来使用。 此模式适用于昂贵的纯函数。
用于配置的可调用对象
__call__ 可以创建灵活的配置对象,这些对象根据调用参数表现不同,同时保持配置状态。
class Configurator:
def __init__(self, default_mode="standard"):
self.mode = default_mode
self.settings = {}
def __call__(self, mode=None, **options):
if mode:
self.mode = mode
self.settings.update(options)
return self
def get_setting(self, key):
return self.settings.get(key, f"default_{key}")
config = Configurator()
config(mode="debug", timeout=100, retries=3)
print(config.mode) # "debug"
print(config.get_setting("timeout")) # 100
此 Configurator 类提供了一个用于配置的流畅接口。 __call__ 方法更新设置并返回 self 以进行链接。
该对象维护配置状态,并且可以多次调用以调整设置,从而提供了一种灵活的配置文件的替代方案。
最佳实践
- 保持可调用语义:使 __call__ 表现得像一个函数
- 记录调用签名:清楚地记录预期的参数
- 考虑 functools.wraps:对于装饰器,保留函数元数据
- 保持状态清洁:仔细管理调用之间的实例状态
- 首选简单函数:仅在需要状态时使用 __call__
资料来源
作者
列出所有 Python 教程。