__set_name__ 是 Python 3.6 引入的描述符协议方法,用于在类创建后自动接收所属类和属性名,避免手动传参;需满足两个前提:描述符类定义该方法且实例为类变量。

__set_name__ 是什么,为什么需要它
在 Python 描述符中,过去要让描述符知道自己被赋给了哪个属性名,只能靠手动传参(比如 MyDescriptor(name="foo")),既啰嗦又容易出错。Python 3.6 引入的 __set_name__ 方法解决了这个问题:解释器会在类创建完成、描述符被赋值给类属性后,自动调用它并传入所属类和属性名。
这意味着你不再需要硬编码属性名,也不用依赖 <strong>get</strong> 中临时推断(比如从 frame.f_code.co_names 解析,极不可靠)。
必须满足的两个前提条件
__set_name__ 不会无条件触发,缺一不可:
- 描述符类必须定义了
__set_name__方法(哪怕只写pass) - 描述符实例必须是类变量(即直接定义在 class 语句块内),不能是运行时通过
setattr(cls, ...)动态添加的
常见错误现象:__set_name<strong></strong> 完全没被调用 → 检查是否漏写了方法,或描述符被放到了 init__ 里成了实例属性。
调用 Cutout.Pro 视觉处理 API 进行背景移除、人像抠图和照片增强,支持文件上传与图片 URL 输入。
典型用法:用属性名初始化描述符内部状态
最常见场景是把属性名存下来,用于后续错误提示、日志或构建键名:
class ValidatedField:
def __init__(self, type_hint=None):
self.type_hint = type_hint
<pre class="brush:php;toolbar:false;">def __set_name__(self, owner, name):
self.name = name # 自动保存为 'age'、'email' 等
self.private_name = f"_{name}"
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, self.private_name, None)
def __set__(self, obj, value):
if not isinstance(value, self.type_hint):
raise TypeError(f"{self.name} must be {self.type_hint.__name__}")
setattr(obj, self.private_name, value)class Person: age = ValidatedField(int) # __set_name__ 自动被调用,name='age' email = ValidatedField(str) # name='email'
注意:owner 参数是类对象(如 Person),可用于检查继承关系或元数据注册;name 才是字符串形式的属性名。
容易忽略的边界情况
- 如果描述符被多次复用在同一个类里(比如
a = ValidatedField(int); b = ValidatedField(int)),每个实例的 __set_name__ 都会被独立调用,name 各不相同 → 安全。
- 若描述符被继承,子类中同名属性会触发子类的
__set_name__,owner 是子类而非父类 → 符合直觉,但要注意逻辑是否依赖父类名。
-
__set_name<strong></strong> 只在类体执行完后调用,此时类尚未完全构建完成(例如不能安全访问 owner.mro__ 中的某些动态属性),尽量只做轻量初始化。
a = ValidatedField(int); b = ValidatedField(int)),每个实例的 __set_name__ 都会被独立调用,name 各不相同 → 安全。__set_name__,owner 是子类而非父类 → 符合直觉,但要注意逻辑是否依赖父类名。__set_name<strong></strong> 只在类体执行完后调用,此时类尚未完全构建完成(例如不能安全访问 owner.mro__ 中的某些动态属性),尽量只做轻量初始化。真正麻烦的是当描述符逻辑依赖属性名,但又想支持别名或动态生成属性时——这时 __set_name__ 已经固定,无法重写,得换思路,比如用工厂函数封装。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










