Python 中的代理模式(Proxy Pattern)
代理模式是一种结构型设计模式,其核心目的是:
为另一个对象提供一个替身或占位符(代理),以控制对这个对象的访问。
形象比喻:就像明星的经纪人——粉丝想找明星,只能通过经纪人(代理)联系,经纪人可以筛选、记录、延迟或增强请求。
代理模式的四种常见类型
- 虚拟代理(Virtual Proxy):延迟加载(懒加载),只有真正需要时才创建真实对象(节省资源)。
- 远程代理(Remote Proxy):代表网络另一端的对象(RPC、Web Service)。
- 保护代理(Protection Proxy):根据权限控制对真实对象的访问。
- 智能代理(Smart Proxy):在访问对象时添加额外行为(如计数、日志、缓存)。
Python 实现示例
1. 虚拟代理(懒加载)—— 最常见场景
适用于加载代价高昂的对象(如大图片、数据库连接、复杂计算)。
class Image:
def __init__(self, filename):
self.filename = filename
print(f"加载图片 {filename}(这可能很慢!)")
# 模拟耗时加载
import time
time.sleep(2) # 模拟 IO 延迟
def display(self):
print(f"显示图片: {self.filename}")
class ProxyImage:
def __init__(self, filename):
self.filename = filename
self._real_image = None # 延迟创建
def display(self):
if self._real_image is None:
self._real_image = Image(self.filename) # 真正需要时才加载
self._real_image.display()
# 使用
if __name__ == "__main__":
image1 = ProxyImage("photo1.jpg")
image2 = ProxyImage("photo2.jpg")
print("代理创建完成,但图片还未加载")
image1.display() # 第一次调用:真正加载并显示
image1.display() # 第二次:直接显示,已缓存
image2.display() # 加载并显示
输出:
代理创建完成,但图片还未加载
加载图片 photo1.jpg(这可能很慢!)
显示图片: photo1.jpg
显示图片: photo1.jpg
加载图片 photo2.jpg(这可能很慢!)
显示图片: photo2.jpg
2. 保护代理(权限控制)
class SensitiveData:
def __init__(self):
self.data = "超级机密信息:银行账户 = 123456789"
def read(self):
print(f"读取数据: {self.data}")
def write(self, new_data):
print(f"写入数据: {new_data}")
self.data = new_data
class ProtectionProxy:
def __init__(self, real_subject: SensitiveData, user_role: str):
self._real_subject = real_subject
self._user_role = user_role
def read(self):
if self._user_role in ["admin", "user"]:
self._real_subject.read()
else:
print("权限不足:无法读取")
def write(self, new_data):
if self._user_role == "admin":
self._real_subject.write(new_data)
else:
print("权限不足:只有管理员可写")
# 使用
data = SensitiveData()
proxy_admin = ProtectionProxy(data, "admin")
proxy_user = ProtectionProxy(data, "user")
proxy_guest = ProtectionProxy(data, "guest")
proxy_admin.read() # 允许
proxy_admin.write("新数据") # 允许
proxy_user.read() # 允许
proxy_user.write("尝试修改") # 拒绝
proxy_guest.read() # 拒绝
3. 智能代理(日志 + 缓存)
class ExpensiveComputation:
def compute(self, x):
print(f"执行昂贵计算 for {x}...")
import time
time.sleep(1)
return x ** x
class SmartProxy:
def __init__(self):
self._real = ExpensiveComputation()
self._cache = {} # 缓存结果
def compute(self, x):
print(f"[代理] 请求计算 {x}")
if x not in self._cache:
self._cache[x] = self._real.compute(x)
print(f"[代理] 缓存新结果")
else:
print(f"[代理] 从缓存返回")
return self._cache[x]
# 使用
proxy = SmartProxy()
print(proxy.compute(5)) # 真正计算
print(proxy.compute(5)) # 缓存命中
print(proxy.compute(10)) # 真正计算
代理模式结构总结
| 角色 | 说明 |
|---|---|
| Subject | 公共接口(Image / SensitiveData) |
| RealSubject | 真实对象 |
| Proxy | 代理对象,控制访问、添加行为 |
| Client | 使用 Subject 接口的代码 |
代理模式 vs 装饰器模式 vs 适配器模式
| 模式 | 目的 | 是否持有真实对象 | 是否改变接口 |
|---|---|---|---|
| 代理 | 控制访问(懒加载、权限、日志) | 是 | 不改变 |
| 装饰器 | 增强功能(添加职责) | 是 | 不改变 |
| 适配器 | 转换接口 | 是 | 改变 |
关键区别:
- 代理和装饰器都保持接口不变,但代理更注重“控制访问”,装饰器更注重“增强行为”。
- 代理常用于资源管理、权限、安全;装饰器常用于功能叠加。
Python 中的实际应用场景
- Web 框架中的数据库连接代理(连接池)
- ORM 中的懒加载属性(如 SQLAlchemy 的关系字段)
- 缓存代理(如 Redis 缓存代理)
- API 客户端的认证代理(自动添加 token)
- 文件访问代理(权限检查)
Pythonic 简化方式
很多时候可以用属性描述符(@property)或第三方库实现代理:
class LazyProperty:
def __init__(self, func):
self.func = func
def __get__(self, obj, cls):
if obj is None:
return self
value = self.func(obj)
setattr(obj, self.func.__name__, value) # 缓存
return value
class BigObject:
@LazyProperty
def heavy_data(self):
print("加载大数据...")
return {"data": "very large"}
obj = BigObject()
print("创建对象")
print(obj.heavy_data) # 第一次加载
print(obj.heavy_data) # 第二次直接返回
总结建议
- 使用代理模式 当你需要控制访问、延迟加载、添加安全检查时。
- 优先使用对象组合 实现代理(Python 推荐)。
- 不要滥用:如果只是增强功能,考虑装饰器;如果是接口转换,用适配器。
如果你想看更高级的例子(如远程代理模拟、结合 asyncio 的异步代理、缓存代理实现),或者与其他模式对比的实际项目案例,欢迎继续问!