CodeWalk

Python __getattribute__实现AOP切面编程

作者:孤独的心 · 2026-05-30 12:55

使用__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__结合可灵活支配。