英文:
Why setting __set__ function for method descriptor does not make it data-descriptor?
问题
我已经开始阅读关于Python中描述符的内容,并创建了一个小测试来查看属性访问的顺序是否与描述的顺序相同。据我理解,当访问对象的属性时,会按照以下顺序查找:
- 在
type(instance).__dict__
中查找 - 如果存在给定名称的数据描述符,则返回其__get__
方法的调用。 - 在
instance.__dict__
中查找。 - 如果在
type(instance).__dict__
中存在非数据描述符,则返回__get__
方法的调用。 - 调用
__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:
- in
type(instance).__dict__
- if data descriptor of given name is present, its__get__
method call is returned - in
instance.__dict__
- if non-data descriptor is present in
type(instance).__dict__
then__get__
method call is returned __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("Nothing to see here...")
A.__dict__['d'].__set__ = __set__
a = A()
a.d = "value"
print(a.d) # prints "value" instead of <bound method ...>
答案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__['d'].__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:
>>> 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 # now the descriptor takes precedence again over a.__dict__
'foo'
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论