魔术方法__call__
__call__
是一个特殊方法(也称为魔术方法),用于使一个类的实例能够像函数一样被调用。当定义了这个方法后,实例对象可以后接括号(即 ()
)来触发调用,这会让实例表现得像函数一样。
- 使实例可调用:允许类的实例像函数一样被调用。
- 保持状态:可以在多次调用之间保持实例的状态(因为实例可以存储属性)。
- 实现装饰器:常用于实现装饰器类(Decorator Class)。
- 模拟函数行为:让对象具备函数的行为,同时保留类的特性(如属性、方法)。
基本语法
class MyClass:def __call__(self, *args, **kwargs):# 定义调用时的行为return "Called with:", args, kwargsobj = MyClass()
result = obj(1, 2, 3, name="Alice") # 触发 __call__
# result = obj() # 触发 __call__
print(result)# ('Called with:', (1, 2, 3), {'name': 'Alice'})
# ('Called with:', (), {})
输出:
('Called with:', (1, 2, 3), {'name': 'Alice'})
关键特点
-
**
*args
和**kwargs
**
__call__
可以接受任意数量的位置参数和关键字参数,类似于普通函数。 -
实例本身仍然是一个对象
即使定义了__call__
,实例仍然可以拥有属性和方法:class Adder:def __init__(self, initial=0):self.total = initialdef __call__(self, x):self.total += xreturn self.totaladd = Adder(10) print(add(5)) # 15(10 + 5) print(add(3)) # 18(15 + 3)
-
用于装饰器
__call__
可以让类的实例作为装饰器使用:class Logger:def __init__(self, func):self.func = funcdef __call__(self, *args, **kwargs):print(f"Calling {self.func.__name__}")return self.func(*args, **kwargs)@Logger def greet(name):return f"Hello, {name}!"print(greet("Alice"))
输出:
Calling greet Hello, Alice!
**__call__
vs __init__
**
方法 | 调用时机 | 用途 |
---|---|---|
__init__ | 实例化时调用(obj = MyClass() ) | 初始化对象属性 |
__call__ | 实例被调用时(obj() ) | 让实例像函数一样执行 |
实际应用场景
-
状态保持的函数(类似闭包)
class Counter:def __init__(self):self.count = 0def __call__(self):self.count += 1return self.countcounter = Counter() print(counter()) # 1 print(counter()) # 2
-
实现策略模式
class Multiply:def __call__(self, a, b):return a * bclass Add:def __call__(self, a, b):return a + bdef calculate(operation, a, b):return operation(a, b)print(calculate(Multiply(), 3, 4)) # 12 print(calculate(Add(), 3, 4)) # 7
-
动态生成对象行为
class Power:def __init__(self, exponent):self.exponent = exponentdef __call__(self, x):return x ** self.exponentsquare = Power(2) cube = Power(3) print(square(4)) # 16 print(cube(3)) # 27
总结
__call__
让类的实例可以像函数一样被调用。- 适用于需要保持状态的函数、装饰器类、策略模式等场景。
- 不同于
__init__
,它控制的是实例的调用行为,而非初始化行为。
如果你想让一个对象既能存储数据又能像函数一样执行操作,__call__
是一个非常有用的工具!