从类或实例设置属性时的查找过程是什么?


问题内容

Nutshell中Python描述
了从类cls.name获取属性时的查找过程,例如,以及从实例获取属性时的查找过程。obj.name

但是我不确定何时设置属性:

设置属性

请注意,仅当您引用属性而不绑定属性时,属性查找步骤才按照上述步骤进行。当您绑定(无论是类或实例)的属性,它的名字是不是特别的(除非
一个__setattr__ 方法,或 __set__ 压倒一切的描述符的方法,截取一个实例属性的绑定),你只影响
__dict__ 该属性条目(分别在类或实例中)。换句话说,对于属性绑定,除了检查 覆盖描述符 之外,不涉及查找过程。

我的问题是:

  • 设置类的属性时,例如“查找”过程是什么cls.name=value

  • 设置对象的属性时,例如“查找”过程是什么obj.name=value

该程序似乎涉及

  • __setattr__ 方法

setattr (自身,名称,值)

在每个绑定属性的请求x.y(通常是赋值语句 x.y=value,但也有,例如setattr(x, 'y', value))上,Python都会调用 x.__setattr__('y', value)。Python始终要求__setattr__在x上绑定任何属性-与__getattr__(在这种意义上__setattr__更接近__getattribute__)有很大的不同。为了避免递归,当
x.__setattr__bindsx的属性时,它必须x.__dict__直接修改(例如,通过x.__dict__[name]=value);甚至更好的是,__setattr__可以将设置委派给超类(通过super(C, x).__setattr__('y', value) 在v3中调用或just super().__setattr__('y', value))。Python会忽略的返回值__setattr__。如果__setattr__不存在(即从继承
object),并且C.y不是重写描述符,则Python通常会转换x.y=zx.__dict__['y']=z

鉴于本书区分了从类和实例中获取属性的查找过程,因此自然地认为从类中设置属性和从实例中设置属性时,“查找”过程是不同的。

但是由于在Python3中,每个类对象实际上都是其元类(例如,type类)的实例,用于从类中设置属性的“查找”过程和用于从实例中设置属性的“查找”过程真的不同吗?

谢谢。


问题答案:

对于x.y = z,Python的查找x.__setattr__的方式绕过__getattribute____getattr__x自己的__dict__,然后调用x.__setattr__('y', z)

自定义__setattr__可以执行任何所需的操作,但这是默认设置:

  1. 搜索type(x).__mro__一个__dict__匹配的属性名称条目。
  2. 如果此搜索找到descr具有__set__方法的描述符(以与查找__set__方式相同的方式查找__setattr__),请调用descr.__set__(x, z)

2a。在非常不寻常的情况下,搜索会找到一个带__delete__但不带的描述符__set__,并引发AttributeError。

  1. 否则,设置x.__dict__['y'] = z或引发AttributeErrorif if xno __dict__。(这使用“ real” __dict__,绕过了诸如此类mappingproxy。)

这适用于类型和其他对象,但要注意的是它们type.__setattr__将拒绝设置用C编写的类的属性,并且type.__setattr__会进行一些额外的内部维护,除非您疯狂地做一些不被支持的事情以绕过它,否则您将不必担心。