Python中的属性拦截与属性管理(`__getattr__`、`__getattribute__`、`__setattr__`、`__delattr__`)实战案例
字数 1012 2025-11-08 10:03:28

Python中的属性拦截与属性管理(__getattr____getattribute____setattr____delattr__)实战案例

1. 问题描述

在Python中,我们可以通过重写特殊方法(如__getattr____getattribute____setattr____delattr__)来拦截对象的属性访问、设置和删除操作。这些方法常用于实现动态属性、属性验证、惰性加载或代理模式。但它们的调用时机和用法容易混淆,需要通过实际案例加深理解。


2. 核心方法区分

  1. __getattr__(self, name)

    • 触发条件:当访问不存在的属性时触发。
    • 常见用途:动态生成属性或实现降级策略(如默认值)。
  2. __getattribute__(self, name)

    • 触发条件:访问任何属性时都会触发(包括存在的属性)。
    • 风险:若实现不当易导致递归调用(如内部需用super().__getattribute__)。
  3. __setattr__(self, name, value)

    • 触发条件:设置属性时触发(包括初始化时的赋值)。
    • 注意:内部赋值需避免直接self.name = value(会递归),应使用super().__setattr__
  4. __delattr__(self, name)

    • 触发条件:删除属性时触发。

3. 实战案例:实现属性访问日志与自动类型验证

假设我们需要一个类,满足以下需求:

  1. 记录所有属性的访问、设置和删除日志。
  2. 对某些属性(如age)进行类型验证(必须为整数)。

步骤1:基础类结构

class LoggedClass:
    def __init__(self):
        # 避免递归,通过__dict__直接赋值
        self.__dict__["_attributes"] = {}
        self.__dict__["_validations"] = {"age": int}  # 需要类型验证的属性

    def _log(self, action, name, value=None):
        print(f"[LOG] {action} attribute '{name}': {value}")

步骤2:重写__setattr__实现类型验证与日志

def __setattr__(self, name, value):
    # 类型验证逻辑
    if name in self._validations:
        if not isinstance(value, self._validations[name]):
            raise TypeError(f"{name} must be {self._validations[name].__name__}")
    
    # 记录日志
    self._log("SET", name, value)
    
    # 实际存储属性(避免递归)
    super().__setattr__(name, value)  # 或 self._attributes[name] = value

步骤3:重写__getattribute__记录属性访问

def __getattribute__(self, name):
    # 先获取属性值(避免递归需用super)
    value = super().__getattribute__(name)
    
    # 记录访问日志(排除内置方法和日志方法本身)
    if not name.startswith("_"):
        self._log("GET", name, value)
    return value

步骤4:重写__getattr__处理不存在的属性

def __getattr__(self, name):
    # 动态返回默认值
    self._log("GET_MISSING", name, "DEFAULT_NONE")
    return None  # 或自定义逻辑(如惰性加载)

步骤5:重写__delattr__记录删除操作

def __delattr__(self, name):
    self._log("DEL", name)
    super().__delattr__(name)

4. 完整代码与测试

class LoggedClass:
    def __init__(self):
        self.__dict__["_attributes"] = {}
        self.__dict__["_validations"] = {"age": int}
    
    def _log(self, action, name, value=None):
        print(f"[LOG] {action} attribute '{name}': {value}")
    
    def __setattr__(self, name, value):
        if name in self._validations and not isinstance(value, self._validations[name]):
            raise TypeError(f"{name} must be {self._validations[name].__name__}")
        self._log("SET", name, value)
        super().__setattr__(name, value)
    
    def __getattribute__(self, name):
        value = super().__getattribute__(name)
        if not name.startswith("_"):
            self._log("GET", name, value)
        return value
    
    def __getattr__(self, name):
        self._log("GET_MISSING", name, "DEFAULT_NONE")
        return None
    
    def __delattr__(self, name):
        self._log("DEL", name)
        super().__delattr__(name)

# 测试
obj = LoggedClass()
obj.name = "Alice"  # 触发SET日志
obj.age = 30        # 正常赋值
print(obj.name)     # 触发GET日志
print(obj.salary)   # 触发GET_MISSING日志
del obj.name        # 触发DEL日志

5. 关键注意事项

  1. 避免递归:在__getattribute____setattr__中,必须通过super()或直接操作__dict__来访问/设置属性。
  2. 性能影响:频繁拦截属性操作可能降低性能,需谨慎使用。
  3. 应用场景:动态代理(如ORM)、属性权限控制、调试工具等。

通过此案例,你可以掌握如何组合使用属性拦截方法,实现灵活的对象行为控制。

Python中的属性拦截与属性管理( __getattr__ 、 __getattribute__ 、 __setattr__ 、 __delattr__ )实战案例 1. 问题描述 在Python中,我们可以通过重写特殊方法(如 __getattr__ 、 __getattribute__ 、 __setattr__ 、 __delattr__ )来拦截对象的属性访问、设置和删除操作。这些方法常用于实现动态属性、属性验证、惰性加载或代理模式。但它们的调用时机和用法容易混淆,需要通过实际案例加深理解。 2. 核心方法区分 __getattr__(self, name) 触发条件 :当访问不存在的属性时触发。 常见用途 :动态生成属性或实现降级策略(如默认值)。 __getattribute__(self, name) 触发条件 :访问任何属性时都会触发(包括存在的属性)。 风险 :若实现不当易导致递归调用(如内部需用 super().__getattribute__ )。 __setattr__(self, name, value) 触发条件 :设置属性时触发(包括初始化时的赋值)。 注意 :内部赋值需避免直接 self.name = value (会递归),应使用 super().__setattr__ 。 __delattr__(self, name) 触发条件 :删除属性时触发。 3. 实战案例:实现属性访问日志与自动类型验证 假设我们需要一个类,满足以下需求: 记录所有属性的访问、设置和删除日志。 对某些属性(如 age )进行类型验证(必须为整数)。 步骤1:基础类结构 步骤2:重写 __setattr__ 实现类型验证与日志 步骤3:重写 __getattribute__ 记录属性访问 步骤4:重写 __getattr__ 处理不存在的属性 步骤5:重写 __delattr__ 记录删除操作 4. 完整代码与测试 5. 关键注意事项 避免递归 :在 __getattribute__ 和 __setattr__ 中,必须通过 super() 或直接操作 __dict__ 来访问/设置属性。 性能影响 :频繁拦截属性操作可能降低性能,需谨慎使用。 应用场景 :动态代理(如ORM)、属性权限控制、调试工具等。 通过此案例,你可以掌握如何组合使用属性拦截方法,实现灵活的对象行为控制。