Python __setattr__ 方法
最后修改于 2025 年 4 月 8 日
这份全面的指南探讨了 Python 的 __setattr__
方法,这个特殊方法控制属性赋值。我们将涵盖基本用法、属性验证、动态属性和实践示例。
基本定义
当尝试对对象进行属性赋值时,会调用 __setattr__
方法。它拦截所有属性赋值,包括 __init__
中的赋值。
主要特点:它接收属性名称和值作为参数,必须手动处理属性存储,并且可以阻止或转换属性赋值。 它为所有属性赋值调用。
基本 __setattr__ 实现
这是一个简单的实现,展示了 __setattr__
如何拦截属性赋值。 它演示了基本结构和要求。
class Person: def __setattr__(self, name, value): print(f"Setting attribute {name} to {value}") super().__setattr__(name, value) p = Person() p.name = "Alice" p.age = 30 print(p.name, p.age)
此示例显示了基本模式:打印消息,然后使用 super()
委托给父类的 __setattr__
。 如果没有此委托,属性将不会被存储。
输出显示在存储之前拦截了两个属性赋值。 这是更高级属性控制的基础。
使用 __setattr__ 进行属性验证
__setattr__
可以在允许设置属性值之前对其进行验证,从而强制执行业务规则或类型约束。
class Temperature: def __setattr__(self, name, value): if name == "celsius": if not isinstance(value, (int, float)): raise TypeError("Temperature must be numeric") if value < -273.15: raise ValueError("Temperature below absolute zero") super().__setattr__(name, value) temp = Temperature() temp.celsius = 25 # Valid # temp.celsius = -300 # Raises ValueError # temp.celsius = "hot" # Raises TypeError
这个温度类验证摄氏度值是否为数字且高于绝对零度。 无效的赋值会在存储发生之前引发异常。
验证发生在调用父类的 __setattr__
之前,从而防止无效状态。 此模式对于领域模型很有用。
只读属性
__setattr__
可以通过防止初始赋值后修改某些属性,使其成为只读属性。
class Constants: def __init__(self): super().__setattr__("_values", {}) self._values["PI"] = 3.14159 def __setattr__(self, name, value): if name in self._values: raise AttributeError(f"Cannot modify {name}") super().__setattr__(name, value) const = Constants() print(const._values["PI"]) # 3.14159 # const.PI = 3.14 # Raises AttributeError const.E = 2.71828 # Allowed
这个常量类将受保护的值存储在字典中,并防止对其进行修改。 仍然可以正常添加新属性。
技巧是在 __init__
中使用 super().__setattr__
来绕过自定义的 __setattr__
以进行初始设置。
动态属性创建
__setattr__
可以动态地转换属性值或在赋值发生时创建派生属性。
class CaseInsensitiveDict: def __setattr__(self, name, value): lower_name = name.lower() if lower_name == "data": super().__setattr__("data", {}) else: self.data[lower_name] = value def __getattr__(self, name): return self.data.get(name.lower()) d = CaseInsensitiveDict() d.Color = "blue" d.COLOR = "red" print(d.color) # "red" (last assignment wins)
这个类似字典的对象以不区分大小写的方式存储属性。 所有赋值都使用小写键进入数据字典。
请注意对“data”属性本身的特殊处理,需要正常存储该属性以避免无限递归。
阻止属性创建
__setattr__
可以完全阻止新属性的创建,使对象严格遵循预定义的模式。
class StrictPerson: __slots__ = ("name", "age") def __setattr__(self, name, value): if name not in self.__slots__: raise AttributeError(f"Cannot add attribute {name}") super().__setattr__(name, value) p = StrictPerson() p.name = "Bob" # Allowed p.age = 40 # Allowed # p.email = "bob@example.com" # Raises AttributeError
此类将 __slots__
与 __setattr__
结合使用,以创建一个严格控制的对象。 只能设置预定义的属性。
__slots__
声明提供内存效率,而 __setattr__
在运行时强制执行模式。
最佳实践
- 始终调用 super().__setattr__: 除非故意阻止存储
- 避免无限递归: 谨慎使用 super() 或直接访问 __dict__
- 记录行为: 清楚地记录任何特殊的赋值逻辑
- 考虑性能: __setattr__ 会增加所有赋值的开销
- 尽可能使用 @property: 对于简单的情况,属性可能更简洁
资料来源
作者
列出所有 Python 教程。