[Python3高阶编程] - 描述器之__set_name__
__set_name__是 Python 3.6 引入的描述器专用钩子方法一、调用时机类定义时不是实例创建时class Validator: def __init__(self, min_val, max_val): self.min_val min_val self.max_val max_val def __set_name__(self, owner, name): print(f 类 {owner.__name__} 定义时调用) print(f owner {owner}) # 类本身 print(f name {name}) # 属性名 age self.name name # 保存属性名 self.private_name f_{name} # 构造私有属性名 def __get__(self, obj, objtypeNone): if obj is None: return self return getattr(obj, self.private_name, None) def __set__(self, obj, value): if not self.min_val value self.max_val: raise ValueError(f{self.name} 必须在 {self.min_val} 到 {self.max_val} 之间) setattr(obj, self.private_name, value) # 类定义时 # 这一行执行时__set_name__ 被调用 class Person: age Validator(0, 150) # ← 在这里调用 __set_name__ height Validator(50, 250) # ← 在这里调用 __set_name__ print(\n类定义完成\n) # 实例化时 # 这里不会再调用 __set_name__ p Person() # 普通实例化 p.age 25 # 调用 __set__不是 __set_name__输出 类 Person 定义时调用 owner class __main__.Person name age 类 Person 定义时调用 owner class __main__.Person name height 类定义完成二、调用流程图class Person: age Validator(0, 150) # ↑ # └── 类定义时Python 内部执行 # validator Validator(0, 150) # validator.__set_name__(Person, age) # Person.age validator时间线 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 类定义阶段 实例化阶段 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ class Person: p Person() age Validator(0, 150) p.age 25 ↓ ↓ __set_name__ 调用 __set__ 调用 (ownerPerson, nameage) (objp, value25) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━三、两个参数的含义def __set_name__(self, owner, name): owner: 描述器所在的类不是实例 name: 描述器被赋值的属性名字符串 参数类型含义ownertype描述器被绑定到的类namestr属性名如age四、为什么需要__set_name__场景在描述器内部知道属性名没有__set_name__时描述器不知道自己的属性名class BadValidator: def __set__(self, obj, value): # 问题来了我怎么知道我叫什么名字 # 无法自动构造私有属性名 setattr(obj, f_{???}, value) # 不知道填什么有了__set_name__描述器可以自动记录属性名class GoodValidator: def __set_name__(self, owner, name): self.name name # 保存 age self.storage_name f_{name} # 构造 _age def __set__(self, obj, value): # 现在知道自己是哪个属性了 setattr(obj, self.storage_name, value)五、什么时候不会调用1. 类定义后动态赋值不会调用class Person: pass # 类定义完成后再赋值不会触发 __set_name__ Person.age Validator(0, 150) # ❌ 不会调用 __set_name__ p Person() p.age 25 # 报错因为 self.name 未定义2. 实例属性赋值不会调用class Person: age Validator(0, 150) # 会调用 __set_name__ p Person() p.weight Validator(0, 200) # ❌ 不会调用这是实例属性不是类属性六、完整实战自动记录属性名class Typed: 类型检查描述器 def __init__(self, expected_type): self.expected_type expected_type def __set_name__(self, owner, name): print(f 注册属性: {owner.__name__}.{name} (类型: {self.expected_type.__name__})) self.name name self.storage_name f_{name} def __get__(self, obj, objtypeNone): if obj is None: return self return getattr(obj, self.storage_name, None) def __set__(self, obj, value): if not isinstance(value, self.expected_type): raise TypeError( f属性 {self.name} 需要 {self.expected_type.__name__} 类型 f但收到了 {type(value).__name__} ) setattr(obj, self.storage_name, value) class User: name Typed(str) # 注册属性: User.name (类型: str) age Typed(int) # 注册属性: User.age (类型: int) score Typed(float) # 注册属性: User.score (类型: float) # 使用 u User() u.name 抚民 # OK u.age 25 # OK # u.age 25 # TypeError: 属性 age 需要 int 类型...七、记忆口诀__set_name__类定义时自动注册属性名类定义时Person.age Validator(...) ↓ 自动调用 __set_name__(Person, age) ↓ 描述器记住我叫 age我在 Person 类里