python setattribute顺序_python 属性查询顺序,数据描述符

数据描述符,属性查找优先级

如果在一个类中定义了 __get__() , __set__(), __delete__() 这三种方法之一,那么这个类是一个描述符。如果这种类只定义了get方法,那么就是一个非数据描述符,反之则是数据描述符。

实例:

class A(object):

def __init__(self):

self.value = None

def __set__(self, instance, value): # self:类A的实例,也是类B的属性a;instance:类B的实例b;value:通过b.a的赋值

print('set: self,instance,value',self,instance,value)

self.value = value

return self.value

def __get__(self, instance, owner):# instance:类B的实例b;owner:类B

print('get: self,instance,owner',self,instance,owner)

return self.value

class B(object):

a = A()

def __init__(self):

self.val = 20

上述代码中,有两个类,A,B。先看类 B,有一个类属性 a , 且 a 是类 A 的实例,我们先来实例化一下类 B ,看一下 类B 和实例 b 的属性:

b = B()

print(b.__dict__)

print(B.__dict__)

{'val': 20}

{'__module__': '__main__', 'a': <__main__.a object at>, '__init__': , '__dict__': , '__weakref__': , '__doc__': None}

可以看出,实例 b 的属性中,只有一个 val ;类 B 的属性中,有一个 a ,且 a 是类 A 的实例化对象。

接下来,我们调用一下 a:

b = B()

print(b.a,B.a)

get: self,instance,owner <__main__.a object at> <__main__.b object at>

get: self,instance,owner <__main__.a object at> None

None None

结果竟然打出来三行话,我们看一下什么意思:

第一行,是通过 b.a 调用产出的。当调用 b.a 时,程序会自动去调用 b.__getattribute__('a') , 也就是 b.__dict__['a'], 即通过对象 b 的字典去查找属性,但是在第一步我们已经知道, 对象 b 只有一个属性 {'val': 20} ,既然在实例b中找不到‘a'。 因为类A 是一个数据描述符,并且 a 是B的类属性,所以会去调用:B.__dict__['a'].__get__(b,B),得到第一行打印数据

第二行,是通过 B.a 调用产出的。当调用 B.a 时,会直接调用 B.__dict__['a'].__get__(None,B),所以中间有个 None

第三行,很简单了,前两次的 return 都是None,就打印了None

现在,我们尝试给 b.a 赋值

b = B()

b.a = 11

print(b.__dict__)

print(B.__dict__)

B.a = 12

print(b.__dict__)

print(B.__dict__)

set: self,instance,value <__main__.a object at> <__main__.b object at> 11

{'val': 20}

{'__module__': '__main__', 'a': <__main__.a object at>, '__init__': , '__dict__': , '__weakref__': , '__doc__': None}

{'val': 20}

{'__module__': '__main__', 'a': 12, '__init__': , '__dict__': , '__weakref__': , '__doc__': None}

可以看出,当调用了 b.a=11 时,调用了 描述符 的__set__() , 但是对象b 的实例属性并没有改变,依然只有 val=20, 同时类B的类属性也没有改变。 但是当调用 B.a = 12 时,类属性 a变成了12,并没有调用 描述符的 __set__() 方法。

如果类属性的描述符对象和实例对象的属性同名,如果查找?

也就是说,如果把类B改成:

class B(object):

a = A()

def __init__(self):

self.val = 20

self.a = 11

此时调用 b.a ,会如何?

当类A是一个数据描述符,也就是说 类A包含 set 方法,此时数据描述符优先级高,所以结果:

set: self,instance,value <__main__.a object at> <__main__.b object at> 11

get: self,instance,owner <__main__.a object at> <__main__.b object at>

11

当类A是一个非数据描述符,也就是说将类A 中的 set 方法删掉或者注释掉,那么实例的字典优先级高,所以会使用 实例字典中的数据,即结果:

11

属性查询优先级:

obj.__getattribute__()

数据描述符

实例的字典

类的字典(4,5排序并不准确,当两者同名且同为类属性时,后声明赋的值,会覆盖前面的赋值,譬如a=4;a=5;执行完成是5,因为程序是从上往下按顺序执行的)

非数据描述符

父类的字典

__getattr__

补充一个顺序的代码:感兴趣的可以按顺序注释掉代码,运行试试

class Quantity1(object):

def __get__(self, instance, owner):

return 2

def __set__(self, instance, val):

pass

class Quantity2(object):

def __get__(self, instance, owner):

return 5

class A(object):

val = 6 # 6 父类属性

x = None

class B(A):

val = Quantity2() # 5 非覆盖型描述符

val = 4 # 4 类属性,4和5排序并不准确,当两者都为类属性时,后声明的变量会覆盖之前的赋值,因为程序是从上上下执行的。

val = Quantity1() # 2 覆盖型描述符

def __init__(self):

super(B, self).__init__()

self.val = 3

def __getattr__(self, name): # 7 __getattr__

return 7

def __getattribute__(self, name): # 1 __getattribute__

return 1

b = B()

print(b.val)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值