代理类通过持有真实对象引用并重载__getattr__实现轻量动态拦截,统一在代理层做权限校验与审计日志,需显式处理特殊方法、异常捕获保障日志完整性,并接受类型/序列化兼容性代价。

代理类怎么拦截并控制原方法调用
核心是让代理对象持有真实对象引用,并在 __getattr__ 或显式定义的方法中做前置检查。不要重写所有方法——用 __getattr__ 动态代理更轻量,也避免漏掉新增方法。
常见错误:直接继承原类或用装饰器修饰类本身,结果绕过代理逻辑;或者在代理里无条件调用 super().__getattr__(name),导致递归崩溃。
- 代理类不继承目标类,只持有一个
self._real_obj - 重载
__getattr__,内部先做权限/审计判断,再通过getattr(self._real_obj, name)获取真实方法 - 若需拦截
__init__或特殊方法(如__str__),必须显式实现,__getattr__不会捕获它们
如何在代理中统一做访问审计和权限校验
审计日志和权限判断不能耦合在业务方法里,得收口到代理层。关键是把校验逻辑抽成可插拔的钩子,比如 check_access(method_name, *args, **kwargs) 和 log_call(method_name, args, result, duration)。
容易踩的坑:在 __getattr__ 里直接执行方法并记录返回值,但没处理异常——一旦目标方法抛错,审计日志就断在半路;还有人把用户身份信息硬编码进代理类,导致无法复用。
- 用
try/except包裹真实方法调用,确保log_call总被执行(无论成功或失败) - 权限检查函数应接收
self._user_context(由代理初始化时传入),而不是从全局或线程变量读取 - 审计字段至少包含:时间戳、方法名、参数(脱敏后)、返回值类型、耗时、是否异常
为什么不用 functools.wraps 而要手动代理
functools.wraps 只解决函数装饰器的元信息透传问题,对类方法代理无效——它不改变调用链,也不提供实例级上下文(如当前用户、租户ID)。代理模式的核心价值在于实例生命周期内维持状态,比如一个 AdminProxy(user=alice) 实例始终代表 alice 的操作上下文。
性能影响常被高估:CPython 下 __getattr__ 带来的开销约 50–100ns,远低于一次数据库查询或 HTTP 请求;但若代理逻辑里做了同步日志写磁盘或远程鉴权,才是真正瓶颈。
-
wraps无法拦截属性访问、无法动态决定是否放行、无法绑定实例状态 - 代理类可配合
__enter__/__exit__做临时权限提升(如with proxy.elevate(): ...) - 想支持异步方法?得额外实现
__aenter__和代理__await__,wraps完全不涉及这些
实际部署时最容易被忽略的兼容性细节
代理类和原类的类型检查、序列化、IDE 自动补全都会出问题。比如 isinstance(proxy, OriginalClass) 返回 False,用 dataclasses.asdict() 处理代理实例会报错,Pydantic 模型验证也可能失败。
这不是 bug,是代理模式的固有代价——你主动切断了类型继承链,换来的是控制力。接受这点,比强行打补丁更稳妥。
- 需要
isinstance通过?加__class__属性代理(危险,慎用)或改用抽象基类 +register - 序列化失败?重写
__getstate__,只返回self._real_obj的状态,或显式定义to_dict() - IDE 补全缺失?在代理类顶部加类型注释:
def __getattr__(self, name) -> Any: ...,配合typing.TYPE_CHECKING导入原类
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











