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 教程。