Python 中的代理模式(Proxy Pattern)
代理模式是一种结构型设计模式,其核心目的是:
为另一个对象提供一个替身或占位符(代理),以控制对这个对象的访问。
形象比喻:就像明星的经纪人——粉丝想找明星,只能通过经纪人(代理)联系,经纪人可以筛选、记录、延迟或增强请求。
代理模式的四种常见类型
- 虚拟代理(Virtual Proxy):延迟加载(懒加载),只有真正需要时才创建真实对象(节省资源)。
- 远程代理(Remote Proxy):代表网络另一端的对象(RPC、Web Service)。
- 保护代理(Protection Proxy):根据权限控制对真实对象的访问。
- 智能代理(Smart Proxy):在访问对象时添加额外行为(如计数、日志、缓存)。
Python 实现示例
1. 虚拟代理(懒加载)—— 最常见场景
适用于加载代价高昂的对象(如大图片、数据库连接、复杂计算)。
classImage:def__init__(self,filename):self.filename=filenameprint(f"加载图片{filename}(这可能很慢!)")# 模拟耗时加载importtime time.sleep(2)# 模拟 IO 延迟defdisplay(self):print(f"显示图片:{self.filename}")classProxyImage:def__init__(self,filename):self.filename=filename self._real_image=None# 延迟创建defdisplay(self):ifself._real_imageisNone: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.jpg2. 保护代理(权限控制)
classSensitiveData:def__init__(self):self.data="超级机密信息:银行账户 = 123456789"defread(self):print(f"读取数据:{self.data}")defwrite(self,new_data):print(f"写入数据:{new_data}")self.data=new_dataclassProtectionProxy:def__init__(self,real_subject:SensitiveData,user_role:str):self._real_subject=real_subject self._user_role=user_roledefread(self):ifself._user_rolein["admin","user"]:self._real_subject.read()else:print("权限不足:无法读取")defwrite(self,new_data):ifself._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. 智能代理(日志 + 缓存)
classExpensiveComputation:defcompute(self,x):print(f"执行昂贵计算 for{x}...")importtime time.sleep(1)returnx**xclassSmartProxy:def__init__(self):self._real=ExpensiveComputation()self._cache={}# 缓存结果defcompute(self,x):print(f"[代理] 请求计算{x}")ifxnotinself._cache:self._cache[x]=self._real.compute(x)print(f"[代理] 缓存新结果")else:print(f"[代理] 从缓存返回")returnself._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)或第三方库实现代理:
classLazyProperty:def__init__(self,func):self.func=funcdef__get__(self,obj,cls):ifobjisNone:returnself value=self.func(obj)setattr(obj,self.func.__name__,value)# 缓存returnvalueclassBigObject:@LazyPropertydefheavy_data(self):print("加载大数据...")return{"data":"very large"}obj=BigObject()print("创建对象")print(obj.heavy_data)# 第一次加载print(obj.heavy_data)# 第二次直接返回总结建议
- 使用代理模式当你需要控制访问、延迟加载、添加安全检查时。
- 优先使用对象组合实现代理(Python 推荐)。
- 不要滥用:如果只是增强功能,考虑装饰器;如果是接口转换,用适配器。
如果你想看更高级的例子(如远程代理模拟、结合 asyncio 的异步代理、缓存代理实现),或者与其他模式对比的实际项目案例,欢迎继续问!