Python __getattribute__实现AOP切面编程
使用__getattribute__实现面向切面编程(AOP)的核心功能:1)属性访问日志记录;2)性能监控(记录属性访问耗时);3)权限控制。说明如何避免__getattribute__无限递归以及super().__getattribute__的正确调用方式。对比装饰器和__getattribute__实现AOP的优劣。
回答
孤独的心
import time
class AspectProxy:
def __init__(self, wrapped, user_role='guest'):
self._wrapped = wrapped
self._role = user_role
def __getattribute__(self, name):
# 避免对内部属性使用代理逻辑
if name.startswith('_'):
return super().__getattribute__(name)
wrapped = super().__getattribute__('_wrapped')
role = super().__getattribute__('_role')
# 1. 权限控制
if name.startswith('admin_') and role != 'admin':
raise PermissionError(f'{role} 无权访问 {name}')
attr = getattr(wrapped, name)
# 只对方法进行包装
if callable(attr):
def timed(*args, **kwargs):
# 2. 日志
print(f'[{time.time():.3f}] 调用 {name}')
# 3. 性能监控
start = time.time()
try:
return attr(*args, **kwargs)
finally:
elapsed = time.time() - start
print(f'{name} 耗时: {elapsed:.4f}s')
return timed
return attr
class BusinessLogic:
def compute(self):
time.sleep(0.1)
return 42
def admin_delete(self):
return 'deleted'
proxy = AspectProxy(BusinessLogic(), 'user')
proxy.compute() # OK,带日志+监控
# proxy.admin_delete() # PermissionError
无限递归避免:访问内部属性(_wrapped、_role)必须用super().__getattribute__()绕过自定义逻辑。
vs 装饰器:
- 装饰器:需手动装饰每个方法,侵入性强
__getattribute__:无需修改原类,统一拦截,但影响所有属性访问(包括非方法属性),性能稍差
vs 元类:元类可在类创建时自动包装方法,与__getattribute__结合可灵活支配。