代理类通过__getattr__拦截不存在的属性访问,结合权限校验(如配置字典或装饰器标记)控制方法调用;需将被代理对象存为实例变量(如self._target),用getattr(self._target, name)安全获取绑定方法,并抛出permissionerror而非静默失败。

代理类怎么拦截并控制方法调用
Python 中没有原生的“代理模式”语法,但可以通过 __getattr__ 或 __getattribute__ 拦截属性访问,再结合权限逻辑实现访问控制。关键不是“代理对象本身多复杂”,而是拦截点选对了没——__getattr__ 只在属性不存在时触发,而 __getattribute__ 每次都走,开销大且容易递归报错。
实操建议:
- 优先用
__getattr__,只对目标方法做拦截,避免干扰__dict__、__class__等内部属性 - 把被代理对象存在实例变量里(如
self._target),别直接继承或混入,否则方法解析顺序会乱 - 权限检查逻辑要前置,别等方法执行完才校验——尤其对修改状态或 IO 的方法
如何动态判断用户是否有权调用某个方法
权限不能硬编码在代理类里,得支持运行时决策。常见做法是用装饰器标记方法权限(如 @requires("admin")),或查配置字典 self._permissions = {"delete_user": "admin", "view_log": "user"}。
注意几个易错点:
- 方法名字符串要和实际调用一致:如果原方法是
def get_profile(self, user_id):,代理里用getattr(self._target, name)拿到的是函数对象,不是字符串名,得靠name参数本身 - 权限校验失败时,别默默返回
None,抛PermissionError更明确,也方便上层捕获 - 不要在
__getattr__里做耗时操作(比如每次查数据库),缓存结果或预加载权限集
为什么直接代理实例方法会丢失 self
这是最常踩的坑:getattr(self._target, name) 返回的是绑定方法(bound method),但如果你写成 return getattr(self._target, name)(*args, **kwargs),它其实已经绑定了 self._target,不会出错;但若误写成 getattr(type(self._target), name).__func__ 这类未绑定形式,就会缺 self 报 TypeError: missing 1 required positional argument。
安全写法只有一条:
- 始终通过
getattr(self._target, name)获取,它自动处理绑定逻辑 - 别手动解包
__func__或用types.MethodType重建,除非你明确需要改变绑定对象 - 测试时用带
print的简单方法验证:比如def test(self): return id(self),看返回的id是否等于self._target的
代理类要不要支持属性读写和特殊方法
大多数访问控制只关心方法调用,但如果你的被代理对象有重要属性(如 .config、.is_active)或需要支持 len()、iter(),就得额外处理。
建议分情况:
- 只读属性:在
__getattr__里统一放行,或白名单式允许(if name in ["config", "version"]:) - 可写属性:加一层
__setattr__拦截,但注意避开代理类自己的字段(比如_target、_user_role),常规做法是先if name.startswith("_") or name in ["_target", "_user_role"]:再 super().__setattr__ - 特殊方法(
__len__、__str__等):Python 不会通过__getattr__调用它们,必须显式定义,否则会 fallback 到代理类自己的实现(通常是默认行为)
真正难的不是写代理,是厘清哪些访问该放行、哪些该审计、哪些该拒绝——权限模型一旦变复杂,代理逻辑很快会变成状态机。先从单个关键方法开始控制,比一上来就代理整个对象更可控。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











