Python 3中如何在成员装饰器中调用另一个成员装饰器
问题分析与解决方案
错误根源
你错误地将@LOGIN_REQUIRED装饰在了ADMIN_REQUIRED这个装饰器函数本身上。LOGIN_REQUIRED是为实例方法设计的装饰器,它期望被装饰的函数第一个参数是MyClass的实例self,但ADMIN_REQUIRED作为装饰器工厂,第一个参数是要装饰的目标函数func。当Python执行这个装饰逻辑时,LOGIN_REQUIRED内部的decorated_function里的self实际上是get_administration_data这个函数对象,而非MyClass的实例,自然找不到_MyClass__check_for_valid_token属性,导致报错。
正确实现方式
方案1:在ADMIN_REQUIRED内部嵌套LOGIN_REQUIRED
去掉ADMIN_REQUIRED上的@LOGIN_REQUIRED装饰,转而在ADMIN_REQUIRED内部先给目标函数应用登录验证装饰,再叠加管理员检查逻辑:
from functools import wraps class MyClass: def _check_for_valid_token(self, username, token): # 将双下划线改为单下划线,避免名称修饰问题(符合Python私有约定) # 原有的登录验证逻辑,验证失败抛出异常 pass def check_if_admin(self): # 管理员验证逻辑,返回布尔值 return True def LOGIN_REQUIRED(func): @wraps(func) def decorated_function(self, *args, **kwargs): self._check_for_valid_token(args[0], args[1]) return func(self, *args, **kwargs) return decorated_function def ADMIN_REQUIRED(func): # 先为目标函数添加登录验证 func_with_login = MyClass.LOGIN_REQUIRED(func) @wraps(func_with_login) def decorated_function(self, *args, **kwargs): is_admin = self.check_if_admin() if not is_admin: raise Exception("无管理员权限") return func_with_login(self, *args, **kwargs) return decorated_function @ADMIN_REQUIRED def get_administration_data(self, username, token): # 返回管理员数据 return "敏感管理员数据"
方案2:将装饰器移到类外部(更清晰)
把装饰器定义在类外,避免类内部装饰器的self混淆问题,同时保持逻辑分离:
from functools import wraps def LOGIN_REQUIRED(func): @wraps(func) def decorated_function(self, *args, **kwargs): self._check_for_valid_token(args[0], args[1]) return func(self, *args, **kwargs) return decorated_function def ADMIN_REQUIRED(func): # 先叠加登录验证 func = LOGIN_REQUIRED(func) @wraps(func) def decorated_function(self, *args, **kwargs): if not self.check_if_admin(): raise Exception("无管理员权限") return func(self, *args, **kwargs) return decorated_function class MyClass: def _check_for_valid_token(self, username, token): # 登录验证逻辑 pass def check_if_admin(self): # 管理员验证逻辑 return True @ADMIN_REQUIRED def get_administration_data(self, username, token): return "敏感管理员数据"
关键注意点
- 不要用实例方法装饰器去装饰装饰器工厂函数,两者的参数结构完全不同;
- 双下划线开头的方法会触发Python的名称修饰(name mangling),改为单下划线
_check_for_valid_token更简洁,也能达到内部私有目的; - 两种方案都保证了
LOGIN_REQUIRED的逻辑先执行,再执行管理员验证,符合你的需求。
内容的提问来源于stack exchange,提问作者RandomGuy
相关产品推荐
相关产品推荐

