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
與最上面非資料描述符的情況一緻;
通過以上的分析由此得出以下結論:
通過類通路屬性時有無資料描述符差別
通過執行個體通路屬性時,遵循
資料描述符級别>執行個體屬性>非資料描述符