Python中的属性拦截与动态属性访问:`__getattr__`、`__getattribute__`与`__setattr__`
字数 635 2025-11-06 22:53:29

Python中的属性拦截与动态属性访问:__getattr____getattribute____setattr__

在Python中,属性拦截是指在访问、设置或删除对象属性时,通过特殊方法进行自定义处理的能力。这三大核心方法构成了Python动态属性访问的基石。

1. __getattr____getattribute__的区别

  • __getattr__:仅在正常属性查找失败时触发。当访问不存在的属性时,Python会调用此方法作为后备机制。
  • __getattribute__每次属性访问都会触发,无论属性是否存在。它完全接管了属性查找过程。

示例演示:

class DynamicClass:
    def __init__(self):
        self.existing_attr = "我是已存在的属性"
    
    def __getattr__(self, name):
        print(f"__getattr__被触发,访问不存在的属性: {name}")
        return f"动态创建的属性: {name}"
    
    def __getattribute__(self, name):
        print(f"__getattribute__被触发,访问属性: {name}")
        return super().__getattribute__(name)  # 必须调用父类方法避免递归

obj = DynamicClass()
print(obj.existing_attr)  # 触发__getattribute__
print(obj.non_existing)   # 先触发__getattribute__,再触发__getattr__

输出结果:

__getattribute__被触发,访问属性: existing_attr
我是已存在的属性
__getattribute__被触发,访问属性: non_existing
__getattr__被触发,访问不存在的属性: non_existing
动态创建的属性: non_existing

2. __setattr__的工作原理

__setattr__每次设置属性时触发,包括在__init__中的初始化赋值。需要特别注意避免递归调用:

class SafeSetAttr:
    def __init__(self):
        self._data = {}  # 使用特殊名称避免递归
        self.value = 10   # 这会触发__setattr__
    
    def __setattr__(self, name, value):
        if name == "_data":  # 允许_data初始化
            super().__setattr__(name, value)
        else:
            print(f"设置属性 {name} = {value}")
            self._data[name] = value  # 存储到内部字典

obj = SafeSetAttr()
obj.new_attr = "hello"  # 触发__setattr__
print(obj._data)        # 查看存储的数据

4. 实际应用场景:动态代理模式

结合使用这些方法可以实现强大的动态代理功能:

class AttributeProxy:
    """将属性访问转发到另一个对象"""
    def __init__(self, target):
        super().__setattr__('_target', target)  # 绕过__setattr__
    
    def __getattr__(self, name):
        return getattr(self._target, name)  # 转发到目标对象
    
    def __setattr__(self, name, value):
        setattr(self._target, name, value)

class RealClass:
    def __init__(self):
        self.x = 100

real = RealClass()
proxy = AttributeProxy(real)
print(proxy.x)      # 通过代理访问属性
proxy.y = 200       # 通过代理设置属性

5. 注意事项与最佳实践

  • __getattribute____setattr__中必须使用super()来避免递归
  • __getattr__应处理不存在的属性,对于已知属性应抛出AttributeError
  • 性能考虑:__getattribute__会影响所有属性访问,需谨慎使用

通过这三个特殊方法的组合使用,可以实现属性验证、延迟加载、动态创建等高级功能,充分体现Python的动态特性。

Python中的属性拦截与动态属性访问: __getattr__ 、 __getattribute__ 与 __setattr__ 在Python中,属性拦截是指在访问、设置或删除对象属性时,通过特殊方法进行自定义处理的能力。这三大核心方法构成了Python动态属性访问的基石。 1. __getattr__ 与 __getattribute__ 的区别 __getattr__ :仅在 正常属性查找失败 时触发。当访问不存在的属性时,Python会调用此方法作为后备机制。 __getattribute__ : 每次属性访问 都会触发,无论属性是否存在。它完全接管了属性查找过程。 示例演示: 输出结果: 2. __setattr__ 的工作原理 __setattr__ 在 每次设置属性 时触发,包括在 __init__ 中的初始化赋值。需要特别注意避免递归调用: 4. 实际应用场景:动态代理模式 结合使用这些方法可以实现强大的动态代理功能: 5. 注意事项与最佳实践 在 __getattribute__ 和 __setattr__ 中必须使用 super() 来避免递归 __getattr__ 应处理不存在的属性,对于已知属性应抛出 AttributeError 性能考虑: __getattribute__ 会影响所有属性访问,需谨慎使用 通过这三个特殊方法的组合使用,可以实现属性验证、延迟加载、动态创建等高级功能,充分体现Python的动态特性。