大家好,我是正在实战各种 AI 项目的程序员晚枫。

🎬 开篇:让类的实例像函数一样调用

你有没有想过,为什么这样写是合法的?

1
2
3
4
5
6
7
8
9
10
11
12
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

这就是 __call__ 方法的魔力:让对象实例可以像函数一样被调用。

今天我们就深入理解 Python 的可调用对象,以及它如何让设计模式更优雅。


🎯 什么是可调用对象?

callable() 函数

Python 提供了 callable() 函数来判断一个对象是否可调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 基本类型的可调用性
print(callable(int)) # True - int 是类型,可调用
print(callable(str)) # True
print(callable(len)) # True - 内置函数
print(callable('hello')) # False - 字符串不可调用
print(callable([1, 2, 3])) # False - 列表不可调用

# 自定义函数和类
def my_func():
pass

class MyClass:
pass

class CallableClass:
def __call__(self):
pass

print(callable(my_func)) # True
print(callable(MyClass)) # True - 类是可调用的(创建实例)
print(callable(MyClass())) # False - 普通实例不可调用
print(callable(CallableClass())) # True - 实现了 __call__ 的实例可调用

Python 中有哪些可调用对象?

类型示例说明
函数def func(): pass普通函数
内置函数len, printPython 内置
方法obj.method绑定方法
int, str, MyClass调用创建实例
类型list[int]Python 3.9+ 泛型
生成器函数def gen(): yield返回生成器
协程函数async def coro(): pass返回协程
实现了 __call__ 的实例CallableClass()自定义可调用对象

🔧 call 方法详解

基础语法

1
2
3
4
5
6
7
8
9
10
class CallableObject:
def __call__(self, *args, **kwargs):
"""定义实例被调用时的行为"""
print(f"被调用,参数: args={args}, kwargs={kwargs}")
return "result"

obj = CallableObject()
result = obj(1, 2, name="Alice")
# 输出: 被调用,参数: args=(1, 2), kwargs={'name': 'Alice'}
print(result) # result

实际应用场景

1. 带状态的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Counter:
"""计数器 - 保存状态的函数"""

def __init__(self, start=0):
self.count = start

def __call__(self, step=1):
self.count += step
return self.count

def reset(self):
self.count = 0

# 使用
counter = Counter(10)
print(counter()) # 11
print(counter()) # 12
print(counter(5)) # 17
counter.reset()
print(counter()) # 1

# 相比闭包的优势:可以重置状态

2. 可配置的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Formatter:
"""可配置的格式化器"""

def __init__(self, prefix="", suffix="", uppercase=False):
self.prefix = prefix
self.suffix = suffix
self.uppercase = uppercase

def __call__(self, text):
result = f"{self.prefix}{text}{self.suffix}"
if self.uppercase:
result = result.upper()
return result

# 创建不同配置的格式化器
quote = Formatter('"', '"')
shout = Formatter(suffix="!", uppercase=True)
code = Formatter("`", "`")

print(quote("Hello")) # "Hello"
print(shout("hello")) # HELLO!
print(code("print")) # `print`

3. 缓存/记忆化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Memoize:
"""记忆化装饰器(类实现)"""

def __init__(self, func):
self.func = func
self.cache = {}

def __call__(self, *args):
if args in self.cache:
print(f"缓存命中: {args}")
return self.cache[args]
result = self.func(*args)
self.cache[args] = result
return result

@Memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))
print(fibonacci(10)) # 缓存命中

4. 验证器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Validator:
"""可配置的验证器"""

def __init__(self, min_val=None, max_val=None,
min_length=None, max_length=None,
required_type=None):
self.min_val = min_val
self.max_val = max_val
self.min_length = min_length
self.max_length = max_length
self.required_type = required_type

def __call__(self, value):
errors = []

if self.required_type and not isinstance(value, self.required_type):
errors.append(f"类型错误: 期望 {self.required_type}, 实际 {type(value)}")

if self.min_val is not None and value < self.min_val:
errors.append(f"值过小: 最小 {self.min_val}, 实际 {value}")

if self.max_val is not None and value > self.max_val:
errors.append(f"值过大: 最大 {self.max_val}, 实际 {value}")

if self.min_length is not None and len(value) < self.min_length:
errors.append(f"长度过短: 最小 {self.min_length}, 实际 {len(value)}")

if self.max_length is not None and len(value) > self.max_length:
errors.append(f"长度过长: 最大 {self.max_length}, 实际 {len(value)}")

return len(errors) == 0, errors

# 创建不同的验证器
age_validator = Validator(min_val=0, max_val=150, required_type=int)
password_validator = Validator(min_length=8, max_length=32)

print(age_validator(25)) # (True, [])
print(age_validator(-5)) # (False, ['值过小: 最小 0, 实际 -5'])
print(password_validator("abc")) # (False, ['长度过短: 最小 8, 实际 3'])

🎨 设计模式实现

策略模式

策略模式定义了一系列算法,让它们可以互换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from abc import ABC, abstractmethod
from typing import List

# 1. 定义策略接口
class SortStrategy(ABC):
@abstractmethod
def sort(self, data: List[int]) -> List[int]:
pass

# 2. 实现具体策略
class BubbleSort(SortStrategy):
def sort(self, data: List[int]) -> List[int]:
arr = data.copy()
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr

class QuickSort(SortStrategy):
def sort(self, data: List[int]) -> List[int]:
if len(data) <= 1:
return data
pivot = data[len(data) // 2]
left = [x for x in data if x < pivot]
middle = [x for x in data if x == pivot]
right = [x for x in data if x > pivot]
return self.sort(left) + middle + self.sort(right)

class PythonSort(SortStrategy):
def sort(self, data: List[int]) -> List[int]:
return sorted(data)

# 3. 上下文类
class Sorter:
def __init__(self, strategy: SortStrategy):
self.strategy = strategy

def set_strategy(self, strategy: SortStrategy):
self.strategy = strategy

def sort(self, data: List[int]) -> List[int]:
return self.strategy.sort(data)

# 使用
data = [64, 34, 25, 12, 22, 11, 90]

sorter = Sorter(BubbleSort())
print("冒泡排序:", sorter.sort(data))

sorter.set_strategy(QuickSort())
print("快速排序:", sorter.sort(data))

sorter.set_strategy(PythonSort())
print("内置排序:", sorter.sort(data))

命令模式

命令模式将请求封装为对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
from typing import Callable, List
import time

class Command:
"""命令接口"""

def execute(self):
raise NotImplementedError

def undo(self):
raise NotImplementedError

class Light:
"""接收者:电灯"""

def on(self):
print("电灯打开")

def off(self):
print("电灯关闭")

class LightOnCommand(Command):
"""具体命令:开灯"""

def __init__(self, light: Light):
self.light = light

def execute(self):
self.light.on()

def undo(self):
self.light.off()

class LightOffCommand(Command):
"""具体命令:关灯"""

def __init__(self, light: Light):
self.light = light

def execute(self):
self.light.off()

def undo(self):
self.light.on()

class RemoteControl:
"""调用者:遥控器"""

def __init__(self):
self.history: List[Command] = []

def execute(self, command: Command):
command.execute()
self.history.append(command)

def undo(self):
if self.history:
command = self.history.pop()
command.undo()

# 使用
light = Light()
remote = RemoteControl()

on_cmd = LightOnCommand(light)
off_cmd = LightOffCommand(light)

remote.execute(on_cmd) # 电灯打开
remote.execute(off_cmd) # 电灯关闭
remote.undo() # 电灯打开(撤销最后的关灯命令)

函数式策略模式

Python 的函数是一等公民,可以更简洁地实现策略模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from typing import Callable, List

# 策略就是函数
def bubble_sort(data: List[int]) -> List[int]:
arr = data.copy()
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr

def quick_sort(data: List[int]) -> List[int]:
if len(data) <= 1:
return data
pivot = data[len(data) // 2]
left = [x for x in data if x < pivot]
middle = [x for x in data if x == pivot]
right = [x for x in data if x > pivot]
return quick_sort(left) + middle + quick_sort(right)

def python_sort(data: List[int]) -> List[int]:
return sorted(data)

# 使用字典注册策略
strategies = {
'bubble': bubble_sort,
'quick': quick_sort,
'python': python_sort,
}

def sort_data(data: List[int], strategy: str = 'python') -> List[int]:
sort_func = strategies.get(strategy, python_sort)
return sort_func(data)

data = [64, 34, 25, 12, 22, 11, 90]
print(sort_data(data, 'bubble'))
print(sort_data(data, 'quick'))
print(sort_data(data, 'python'))

📊 性能对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import time
from functools import wraps

# 函数实现
def timer_func():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner

# 类实现
class TimerClass:
def __init__(self):
self.count = 0

def __call__(self):
self.count += 1
return self.count

import timeit

func_timer = timer_func()
class_timer = TimerClass()

func_time = timeit.timeit('func_timer()', globals=globals(), number=1000000)
class_time = timeit.timeit('class_timer()', globals=globals(), number=1000000)

print(f"闭包实现: {func_time:.4f}s")
print(f"类实现: {class_time:.4f}s")
# 类实现通常略慢,但更清晰易读

⚠️ 避坑指南

陷阱 1:混淆 callinit

1
2
3
4
5
6
7
class BadExample:
def __call__(self, value):
"""这是被调用时执行,不是初始化时"""
print(f"Called with {value}")

obj = BadExample() # 只调用 __init__
obj("hello") # 调用 __call__

陷阱 2:忘记返回值

1
2
3
4
5
6
7
8
9
10
11
class Counter:
def __init__(self):
self.count = 0

def __call__(self):
self.count += 1
# ❌ 忘记 return

counter = Counter()
result = counter()
print(result) # None - 意料之外

陷阱 3:在 call 中递归调用

1
2
3
4
5
6
7
8
9
10
11
12
13
class Recursive:
def __init__(self):
self.count = 0

def __call__(self, n):
self.count += 1
if n > 0:
return self(n - 1) # ✅ 正确:通过 self 调用
# return self.__call__(n - 1) # 也可以,但不推荐

r = Recursive()
r(5)
print(r.count) # 6

🎯 本讲总结

通过本讲,我们掌握了:

知识点核心要点
callable()判断对象是否可调用
call让实例可以像函数一样调用
可调用对象优势保存状态、继承、可扩展
策略模式用函数或类实现可互换算法
命令模式封装请求为对象,支持撤销

记住这句话

可调用对象结合了函数和类的优点,让代码既灵活又可维护。


📚 推荐教材

《Python 编程从入门到实践(第 3 版)》 | 《流畅的 Python(第 2 版)》 | 《CPython 设计与实现》

学习路线: 零基础 → 《从入门到实践》 → 《流畅的 Python》 → 本门课程 → 《CPython 设计与实现》


🎓 加入《流畅的 Python》直播共读营

学到这里,如果你想系统吃透这本书——欢迎加入我的直播共读课。

  • 每周直播精讲,逐章拆解核心知识点
  • 专属学习群,随时答疑交流
  • 试运营特惠:499 元299 元

👉 【立即报名《流畅的 Python》共读课】https://mp.weixin.qq.com/s/ivHJwn1nNx5ug4TFrapvGg


🔗 课程导航

上一讲:生成器与协程 | 下一讲:对象引用与可变性


💬 联系我

平台账号/链接
微信扫码加好友
微博@程序员晚枫
知乎@程序员晚枫
抖音@程序员晚枫
小红书@程序员晚枫
B 站Python 自动化办公社区

主营业务:AI 编程培训、企业内训、技术咨询

🎓 AI 编程实战课程

想系统学习 AI 编程?程序员晚枫的 AI 编程实战课 帮你从零上手!

fluent-python.png