【问题标题】:What is the lookup procedure when setting an attribute from a class or from an instance?从类或实例设置属性时的查找过程是什么?
【发布时间】:2017-12-09 19:28:03
【问题描述】:

Python in a Nutshell 描述了the lookup procedure when getting an attribute from a class, e.g. cls.name and the lookup procedure when getting an attribute from an instance, e.g. obj.name

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

设置属性

请注意,属性查找步骤仅按照刚才的描述发生 当您引用一个属性时,而不是当您绑定一个属性时。什么时候 您绑定(在类或实例上)名称为 不是特别的(除非__setattr__ 方法,或__set__ 重写描述符的方法,拦截绑定 实例属性),您只影响 __dict__ 条目 属性(分别在类或实例中)。换一种说法, 对于属性绑定,不涉及查找过程,除了 用于检查覆盖描述符

我的问题是:

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

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

程序似乎涉及

  • __setattr__ 方法

    __setattr__(self, name, value)

    在每次请求绑定属性x.y(通常是一个赋值语句 x.y=value,还有,例如,setattr(x, 'y', value)),Python 调用 x.__setattr__('y', value)。 Python 总是调用__setattr__ x 上的属性绑定——与__getattr__ 的主要区别(__setattr__ 是 在这个意义上更接近__getattribute__)。为了避免递归,当 x.__setattr__绑定x的属性,必须直接修改x.__dict__(例如, 通过x.__dict__[name]=value);更好的是,__setattr__ 可以委托 设置为超类(通过调用super(C, x).__setattr__('y', value) 或者,在 v3 中,只是 super().__setattr__('y', value))。 Python 忽略 __setattr__ 的返回值。如果__setattr__ 不存在(即,继承自 object),而 C.y 不是覆盖描述符,Python 通常会翻译 x.y=z 进入x.__dict__['y']=z

  • 覆盖描述符的__set__ 方法(所以我问Why does `object.__get__` have an argument `owner` for the owner class, while `object.__set__` doesn't?

  • 属性的__dict__ 条目(分别在类或实例中)

Given that the book distinguishes the lookup procedures for getting an attribute from a class and from an instance,很自然地认为从类设置属性和从实例设置属性时“查找”过程是不同的。

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

谢谢。

【问题讨论】:

  • 非常简单:它分配给该类的属性。它不会进入基类。
  • 书中的引述似乎没有那么简单。
  • @Tim 您的问题在第二个引用的最后一句中得到了回答:“换句话说,对于属性绑定,除了检查覆盖描述符之外,不涉及查找过程。 ".
  • 我不确定我是否理解那句话,并且我认为那句话不能代替整个段落,因为该段落暗示仍有一些“查找”程序,而那句话也错过了@987654356 @ 段落中的方法。 @ChristianDean
  • @Tim “你只影响属性的 __dict__ 条目”有什么复杂之处?

标签: python python-3.x


【解决方案1】:

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

自定义__setattr__ 可以随心所欲,但这是默认值:

  1. type(x).__mro__ 中搜索与属性名称匹配的__dict__ 条目。

  2. 如果此搜索找到带有__set__ 方法的描述符descr(查找__set__ 的方式与我们查找__setattr__ 的方式相同),请调用descr.__set__(x, z)

    2.a.在搜索找到带有__delete__ 但没有__set__ 的描述符的极不寻常的情况下,请提出AttributeError

  3. 否则,设置x.__dict__['y'] = z,或者如果x 没有__dict__,则引发AttributeError。 (这使用了“真实的”__dict__,绕过了 mappingproxy 之类的东西。)

这适用于类型和其他对象,需要注意的是 type.__setattr__ 将拒绝设置用 C 编写的类的属性,而 type.__setattr__ 会进行一些额外的内部维护,除非你做一些疯狂且不受支持的事情来绕过它。

【讨论】:

  • 谢谢。 (1) 第一步是否意味着在type(x).__mro__ 中找到一个类属性名为y 的类?如果是并且我们找到一个可以是type(x)的祖先类的类,为什么在步骤2的descr.__set__(x, z)和步骤3的x.__dict__['y'] = z中设置实例属性x.y,而不是设置找到的类的类属性y? (2) 步骤 3 是否发生在步骤 1 中的搜索未能找到任何类时,或者当步骤 1 中的搜索找到某个类但找到的属性不是步骤 2 中的描述符时?
  • (3) 如果您可以为每种情况添加一些示例,这将有助于理解。 (4) 你是根据一些文档还是实际的代码实现来回复的?
  • @Tim: 1) 这就是语言的设计方式。默认情况下,设置对象的属性始终是对该对象的操作,即使存在现有的类属性。 2) 任何一个。
  • 谢谢。 “默认情况下,设置对象的属性始终是对该对象的操作,即使存在现有的类属性。”但是,步骤 1“搜索 type(x).__mro__ 以查找与属性名称匹配的 __dict__ 条目”似乎搜索type(x).__mro__ 中的类,其类属性为 y。可能我理解错了,那么第 1 步是什么意思?
  • @Tim:它正在寻找一个描述符来委派操作,而不是一个属性来分配。
猜你喜欢
  • 2017-12-09
  • 2016-12-01
  • 1970-01-01
  • 2017-12-08
  • 2014-10-11
  • 1970-01-01
  • 2022-12-09
  • 2011-01-07
相关资源
最近更新 更多