python 新式类引进了内建属性__getattribute__,访问属性都要先通过内建属性__getattribute__,接下来谈谈类和实例分别访问属性的过程
class DevNull:
def __init__(self,initval = None,name='var'):
self.val = initval
self.name = name
def __get__(self, instance, owner):
#self指的是RevealAccess实例,instance代表被代理的类的实例,owner代表被代理的类
print("获取..",self.name)
return self.val
def __set__(self, instance, value):
print("设置值:",self.name)
self.val = value
class MyClass(object):
x = DevNull(1000,"var 'x'")
def foo(self):
print("hello world")
m=MyClass()
m.x
输出:
获取.. var 'x'
1000
x属性是数据描述符,DevNull同时拥有 set 和 __get__方法,实例访问x, getattribute__会调用数据描述符的__get 方法
m.x=1
m.x
输出:
设置值: var 'x'
获取.. var 'x'
1
上面m.x=1触发了数据描述符的__set__ 方法,修改了DevNull中self.val的值。所以__get__返回的值也发生了变化。
print(m.__dict__)
输出 {}
print(MyClass.x)
MyClass.x=1
print(MyClass.x)
输出:
获取.. var 'x'
1000
1
上面通过类访问x,第一次通过数据描述符DevNull 的__get__,而第二次访问x,没有经过__get__ 并且类对x的赋值并没有触发数据描述符的__set__方法;这说明类对x赋值,把之前类中数据描述符属性x给覆盖了,所以再次通过类访问x,没有经过__get__.
接下来分析非数据描述符的情况
class DevNull:
def __init__(self,initval = None,name='var'):
self.val = initval
self.name = name
def __get__(self, instance, owner):
#self指的是RevealAccess实例,instance代表被代理的类的实例,owner代表被代理的类
print("获取..",self.name)
return self.val
# def __set__(self, instance, value):
# print("设置值:",self.name)
# self.val = value
class MyClass(object):
x = DevNull(1000,"var 'x'")
def foo(self):
print("hello world")
m=MyClass()
m.x
输出:
获取.. var 'x'
1000
可以看出和数据描述符的情况一直
m.x=1
print(m.x)
输出:
1
对于非数据描述符x,实例修改x的值后,访问x, 没有经过__get__,非数据描述符x被实例属性x覆盖了。
print(m.__dict__)
输出:
{'x': 1}
接下来看下类访问非数据描述符x
print(MyClass.x)
MyClass.x=1
print(MyClass.x)
输出:
获取.. var 'x'
1000
1
与最上面非数据描述符的情况一致;
通过以上的分析由此得出以下结论:
通过类访问属性时有无数据描述符区别
通过实例访问属性时,遵循
数据描述符级别>实例属性>非数据描述符