You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何用setattr为类添加描述符时__set_name__未被调用?

动态添加描述符时__set_name__不调用的原因及解决方法

原因解析

set_name__是Python在类定义阶段自动触发的方法:当你在类体内直接定义描述符属性时,Python会在类初始化完成前,遍历类的所有属性,对每个符合描述符协议的实例自动调用__set_name,传入所属类和属性名。

但用setattr动态添加描述符是在类定义完成之后执行的,此时类的初始化流程已经结束,Python不会再主动触发__set_name__的调用——这是Python的语言设计规则,不是你的操作错误。

解决方法

你可以通过两种方式解决这个问题:

1. 手动调用__set_name__

动态添加描述符后,手动触发该方法,传入所属类和属性名:

class MyDescriptor:
    def __set_name__(self, owner, name):
        self._owner = owner
        self._name = name

    def __get__(self, instance, owner):
        return self._name


class MyClass:
    a = MyDescriptor()


# 动态添加时手动调用__set_name__
desc_b = MyDescriptor()
setattr(MyClass, 'b', desc_b)
desc_b.__set_name__(MyClass, 'b')

mc = MyClass()
print(mc.a)  # 输出 'a'
print(mc.b)  # 输出 'b'

2. 修改描述符,通过__init__传入名称

给描述符添加__init__方法,允许初始化时直接传入属性名,同时保留__set_name__的自动触发逻辑:

class MyDescriptor:
    def __init__(self, name=None):
        self._name = name

    def __set_name__(self, owner, name):
        # 只有初始化没传名称时,才用自动获取的属性名
        if self._name is None:
            self._name = name
        self._owner = owner

    def __get__(self, instance, owner):
        return self._name


class MyClass:
    a = MyDescriptor()  # 类定义阶段自动触发__set_name__,设置_name为'a'


# 动态添加时直接传入名称
setattr(MyClass, 'b', MyDescriptor('b'))

mc = MyClass()
print(mc.a)  # 输出 'a'
print(mc.b)  # 输出 'b'

补充说明

两种方法各有适用场景:手动调用更贴合Python描述符的原生设计,适合少量动态添加的情况;修改__init__的方式更适合批量动态添加场景,不用每次都手动触发__set_name__。

内容的提问来源于stack exchange,提问作者user9413641

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 12:03:36