Python中数据描述符的优先级是如何工作的?

2 投票
1 回答
1111 浏览
提问于 2025-04-17 15:24

我在网上看到了一些内容

对于对象来说,背后的机制在于 object.__getattribute__,它会把 b.x 转换成 type(b).__dict__['x'].__get__(b, type(b)).

这个实现通过一个优先级链来工作,给数据描述符比实例变量更高的优先级而实例变量又比非数据描述符优先级高,如果有提供 getattr,它的优先级最低。

我不太明白“给数据描述符比实例变量更高的优先级”和“实例变量比非数据描述符优先级高”是什么意思

有没有人能给我一个例子,看看在代码中哪个东西的优先级更高

1 个回答

6

每个对象都有一个属性字典,这个字典里包含了它的变量和函数。提到这些字典时:

If an instance’s dictionary has an entry with the same name as a data descriptor,    
the data descriptor takes precedence. If an instance’s dictionary has an entry with
the same name as a non-data descriptor, the dictionary entry takes precedence.

这就是他们在说的内容。

为了展示这一点:

#data descriptor
class A(object):
   def __get__(self, obj, type):
       print "hello from get A"
   def __set__(self, obj, val):
       print "hello from set A"
#non data descriptor
class B(object):
   def __get__(self, obj, type):
       print "hello from get B"

class C(object):
   #our data descriptor
   a = A()
   #our non data descriptor
   b = B()

>>> c = C()
>>> c.a
hello from get A
>>> c.b
hello from get B
>>> c.a = 0
hello from set A
>>> c.a          #notice after reassignment, A.__get__ is still called
hello from set A
>>> c.b = 0     #instance variable with the same name as the non data-descriptor
>>> c.b         #notice how B.__get__ isn't called anymore
0

基本上,这段话的意思是,当你为一个对象自定义了 __get____set__ 方法(这叫做数据描述符),那么在访问或修改属性时,就会调用你自定义的方法,而不是默认的方法。如果只自定义了 __get__ 方法(这叫做非数据描述符),那么实例可以重新赋值给一个实例变量。

举个例子,当你执行 g.x = 0 时:如果 x 是一个数据描述符,那么就会调用 x 自定义的 __set__ 方法;如果 x 是一个实例变量或者非数据描述符,那么就会调用默认的行为。

对于数据描述符来说,类会控制对变量的所有访问和修改。所有对数据描述符类型变量的访问都会经过 __get____set__ 方法。所以当你执行 c.a = 0 时,会调用 A.__set__,并且 c.a 的值会按照类定义的方式被改变。你无法创建一个不是类型 A 的实例变量 'c.a'。

而对于非数据描述符来说,类只控制访问,所以当你执行 c.b = 0 时,由于没有定义 __set__,就会创建一个新的实例变量(这是默认行为)。在设置变量时没有自定义的行为,因此你可以创建一个同名的实例变量,而不需要 __get__ 的行为。

他们提到的优先级是指这两者的动态关系。数据描述符会始终调用 __get____set__,因此无法创建同名的实例变量。而非数据描述符只会在创建同名的实例变量之前调用 __get__

撰写回答