__set_name__ 是描述符协议方法,在类定义阶段、类对象创建完成前被自动调用,用于获取属性名和所属类;它不触发于实例化或赋值时,且仅当描述符作为类变量直接定义在类体中才生效。

__set_name__ 是什么,它在什么时候被调用?
__set_name__ 是一个描述符协议方法,在类体执行完毕、类对象创建完成前被 Python 自动调用。它不是在实例化时触发,而是在类定义阶段——也就是解释器构建类字典、处理类属性时调用的。这意味着你无法靠 __init__ 或实例访问来获取属性名,必须依赖这个钩子。
常见错误是把它和 __set__ 混用:后者只在给实例属性赋值时触发,且此时属性名早已丢失;而 __set_name__ 是唯一能让你在类定义期就捕获属性名的机制。
怎么写一个能用 __set_name__ 的描述符?
必须满足两个条件:一是定义了 __set_name__ 方法,二是该对象被用作类变量(即直接写在 class body 里)。Python 会自动把所属类对象和属性名传给它。
-
__set_name__(self, owner, name)的name参数就是你在类里写的那个变量名,比如age = MyDescriptor()中的"age" -
owner是当前正在定义的类(尚未完全构建好,但已是type实例),可用于检查继承关系或元信息 - 不能在函数内动态创建描述符并赋值给类变量——那会绕过描述符协议,
__set_name__根本不会被调用
示例:
class Field:
def __set_name__(self, owner, name):
self.name = name # 保存属性名,供后续 __get__/__set__ 使用
print(f"绑定到类 {owner.__name__} 的属性 {name}")
<p>class Person:
name = Field() # 触发 <strong>set_name<strong>(Field(), Person, "name")
age = Field() # 触发 </strong>set_name</strong>(Field(), Person, "age")</p>
为什么有时候 __set_name__ 没被调用?
最常踩的坑是“看起来像描述符,其实不是”。以下情况会导致 __set_name__ 完全不执行:
- 把描述符实例赋值给实例属性:
p = Person(); p.name = Field()—— 这只是普通赋值,不走描述符协议 - 用 property 或 @property 包裹描述符:
name = property(Field())——property本身是描述符,它会接管访问,你的Field根本不会被设为类属性 - 在
__init__或其他方法里动态设置类属性:Person.new_field = Field()—— 此时类已创建完毕,Python 不再补调__set_name__ - 描述符没被直接写在类体中,而是通过字典更新:
Person.__annotations__['x'] = Field()—— 同样跳过协议
__set_name__ 和 __init__ 配合使用的典型场景
实际项目中,__set_name__ 常用于字段注册、ORM 映射或验证逻辑初始化。关键点在于:属性名必须在类创建时确定,之后才能用于生成数据库列名、序列化 key 或类型检查。
- 不要在
__set_name__里做耗时操作(如连接数据库),它发生在 import 阶段,会影响模块加载速度 - 如果需要延迟初始化,可以把逻辑收进一个标记位,首次
__get__或__set__时再触发 - 多个同类型描述符共享同一个类(如多个
StringField),__set_name__是区分它们的唯一可靠依据
复杂点在于:类还没完成构建,所以不能安全地访问 owner.__mro__ 中父类的同名描述符,也不能假设 owner 已有某个方法。所有依赖类结构的操作,最好推迟到第一次实例访问时再判断。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











