为什么为方法描述符设置__set__函数不会使其成为数据描述符?

huangapple go评论64阅读模式
英文:

Why setting __set__ function for method descriptor does not make it data-descriptor?

问题

我已经开始阅读关于Python中描述符的内容,并创建了一个小测试来查看属性访问的顺序是否与描述的顺序相同。据我理解,当访问对象的属性时,会按照以下顺序查找:

  1. type(instance).__dict__中查找 - 如果存在给定名称的数据描述符,则返回其__get__方法的调用。
  2. instance.__dict__中查找。
  3. 如果在type(instance).__dict__中存在非数据描述符,则返回__get__方法的调用。
  4. 调用__getattr__

现在,如果方法是给定类的实例的非数据描述符,那么通过给该描述符提供__set__函数/方法,可以将其变为数据描述符,因此,例如阻止或操纵此函数的覆盖将不起作用。不幸的是,这并不起作用,我的问题很简单:为什么会这样?Python是否在类初始化时“标记”数据和非数据描述符,还是我做错了什么?

class A:
    def d(self):
        pass

def __set__(self, instance, value):
    print("Nothing to see here...")

A.__dict__['d'].__set__ = __set__
a = A()
a.d = "value"
print(a.d)  # 输出"value"而不是<bound method ...>
英文:

I've started reading about descriptors in Python and created small test to see whether the order of attribute access is same as it is described. As I understand when attribute of object is accessed it is looked in following order:

  1. in type(instance).__dict__ - if data descriptor of given name is present, its __get__method call is returned
  2. in instance.__dict__
  3. if non-data descriptor is present in type(instance).__dict__ then __get__ method call is returned
  4. __getattr__ is called.

Now, if method is non-data descriptor of type instance of given class, than by giving this descriptor __set__ function/method it would make it data descriptor, hence overwriting such function could be for example blocked or manipulated. Unfortunately that does not work and my question is simply: why is that a case? Does Python "marks" data and non-data descriptors at class initialization or I've done something wrong?

class A:
    def d(self):
        pass

def __set__(self, instance, value):
    print(&quot;Nothing to see here...&quot;)



A.__dict__[&#39;d&#39;].__set__ = __set__
a = A()
a.d = &quot;value&quot;
print(a.d) # prints &quot;value&quot; instead of &lt;bound method ...&gt;

答案1

得分: 2

A.__dict__['d'].__set__ = __set__,在这种情况下,这只是一种复杂的写法,实际上等同于A.d.__set__ = __set__,它在实例上设置了一个魔术方法。

在Python中,这种写法是不起作用的,因为魔术方法只会在类型上进行查找。而你不能在类型上进行设置,因为在这里类型只是内置函数type。

然而,可以动态地将非数据描述符更改为数据描述符:

class MyDescriptor:
    def __get__(self, instance, owner=None):
        return 'foo'

class A:
    f = MyDescriptor()

a = A()
a.f  # 输出 'foo'
a.f = 'foo2'
a.f  # 输出 'foo2'

MyDescriptor.__set__ = lambda self, instance, value: None
a.f  # 现在描述符再次优先于a.__dict__

以上是翻译好的内容,请确认是否满意。

英文:

A.__dict__[&#39;d&#39;].__set__ = __set__, which is just a complicated way of writing A.d.__set__ = __set__ in this case, is setting a magic method on the instance.

This is not working in Python, magic methods are only looked up on the type. And you can't set it on the type, because the type is just the builtin function type here.

However, it is possible to change a non-data descriptor into a data descriptor dynamically:

&gt;&gt;&gt; class MyDescriptor:
...     def __get__(self, instance, owner=None):
...         return &#39;foo&#39;
... 
&gt;&gt;&gt; class A:
...     f = MyDescriptor()
... 
&gt;&gt;&gt; a = A()
&gt;&gt;&gt; a.f
&#39;foo&#39;
&gt;&gt;&gt; a.f = &#39;foo2&#39;
&gt;&gt;&gt; a.f
&#39;foo2&#39;
&gt;&gt;&gt; MyDescriptor.__set__ = lambda self, instance, value: None
&gt;&gt;&gt; a.f  # now the descriptor takes precedence again over a.__dict__
&#39;foo&#39;

huangapple
  • 本文由 发表于 2023年8月9日 05:44:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76863390.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定