为什么Python描述符适用于类级属性而不适用于实例级属性。
class PropDescriptor:
def __init__(self,*args):
print("Init {} {}".format(type(self),args))
self.value = 0
def __get__ (self,instance,owner):
print("get using descriptor")
return instance.instance_att
def __set__(self, instance, value):
print("set using descriptor")
instance.instance_att = value
class TestClass:
class_att = PropDescriptor()
def __init__(self):
self.instance_att = PropDescriptor()
t = TestClass()
print("set instance level...")
t.instance_att = 3
print("\nget instance level...")
print(t.instance_att)
print("\nset class level...")
t.class_att = 4
print("\nget class level...")
print(t.class_att)
输出:
Init <class '__main__.PropDescriptor'> ()
Init <class '__main__.PropDescriptor'> ()
set instance level...
get instance level...
3
set class level...
set using descriptor
get class level...
get using descriptor
4
看起来描述符不用于instance_att
。
我发现了这个identical问题,但它没有真正回答问题所有答案都引用了帖子中的第二个问题。
实例绑定
如果绑定到对象实例,则转换a.x
进入通话:
type(a).__dict__['x'].__get__(a, type(a))
。
但:
(我的代码等效)语句type(t).__dict__["instance_att"]
的第一部分引发 KeyError ,因为type(t).__dict__
没有这样的属性。它是一个实例级别的att。不是吗?
我错过了什么?
答案 0 :(得分:1)
对此我几乎没有答复,但我会尽力解释。这两个问题的答案都在于在内部为python实现属性查找和设置的方式。
1。为什么没有为实例级属性调用描述符?
一旦您了解了a.x = somevalue
在内部的翻译方式,那么答案很简单。当您使用dot
运算符(例如a.x="somevalue"
)设置属性时,它会转换为a.__setattribute__(x, somevalue)
,进而会转换为type(a).__dict__['x'].__set__(x, somevalue)
。
现在考虑您的示例self.instance_att
是在__init__
中定义的,因此它不在type(a).__dict__
中出现,__set__
是在类级别定义的属性的词典,因此是描述符的{ {1}}永远不会被呼叫。
2。为什么只在类级别定义描述符?
我不是百分百确定,但这是我的理解,由于__getattribute__
和__setattribute__
的实现方式,如果在实例级别定义描述符,则描述符将根本无法工作(如上所述),因此它们仅被定义为类属性。
此外,我相信要使描述符适用于实例属性,您将必须为该类覆盖__getattribute__
和__setattribute__
。这就是看起来最简单的版本__getattribute__
被覆盖的样子。
def __getattribute__(self, attr):
attr_val = super().__getattribute__(attr)
attr_type = type(attr_val)
if hasattr(attr_type, '__get__'):
return attr_type.__get__(attr_val, self, type(self))
return attr_val
这是相同的演示
$ python3 -i instance_descriptors.py
>>> t = TestClass()
>>> t.instance_att
get using descriptor
200
>>>
以类似的方式,您还需要覆盖__setattribute__
才能使属性设置起作用。
希望这会有所帮助!
我强烈建议您通过下面的python官方文档进行描述符说明,以完全理解该概念。
答案 1 :(得分:0)
快速回答:t.instance_att
由object.__getattribute__
处理,其定义只是忽略了__get__
和__set__
方法的价值。 t
的实例字典。
更长的答案从解释如何处理属性查找开始。它们不是内置于语言中,而是由某些类的__getattribute__
方法处理。很少被覆盖,这几乎总是意味着object.__getattribute__
,它由_PyObject_GenericGetAttrWithDict
在C中实现。
我不是在这里详细解释这个算法,而是引用https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/,它有很棒的图解释对象属性查找和类属性查找。
Stack Overflow至少有一个答案,包括图表,但我现在无法找到它,所以我为场外参考道歉。如果我能找到它,我将包含一个链接(或者可能将其作为副本关闭)。