通常,一個 descriptor
是具有
“
綁定行為
”
的對象屬性。所綁定行為可通過
descriptor
協(xié)議被自定義的
__get__() ,
__set__()
和
__delete__()
方法重寫。如果一個對象的上述三個方法任意一個被重寫,則就可被稱為 descriptor
。
屬性的默認(rèn)操作是從對象字典中獲取、設(shè)置和刪除一個屬性。例如,a.x
有一個查找鏈,先
a.__dict__['x']
,若沒有則
type(a).__dict__['x']
,若沒有增往上查找父類直到元類。如果查找鏈中,對象被定義了 descriptor
方法,
Python
就會覆蓋默認(rèn)行為。
Descriptor
是一個強大的工具,雖然開發(fā)者不常接觸到它,但它其實就是類、屬性、函數(shù)、方法、靜態(tài)方法、類方法以及
super()
背后的運行機制。
Descriptor 協(xié)議
三個方法原型如下所示:
descr.__get__(self, obj, type=None) --> value
descr.__set__(self, obj, value) --> None
descr.__delete__(self, obj) --> None
數(shù)據(jù) descriptor
是同時具有
__get__()
和
__set__()
方法的對象,若只有
__get__()
方法,則為非數(shù)據(jù) descriptor
。如果實例字典中有和數(shù)據(jù)
descriptor
同名的入口,則數(shù)據(jù)
descriptor
優(yōu)先級更高。相反,非數(shù)據(jù)
descriptor
優(yōu)先級低。
讓
__set__()
方法拋出異常,就能創(chuàng)建一個只讀數(shù)據(jù) descriptor
。
調(diào)用 descriptor
descriptor
可以直接通過方法名調(diào)用。例如,
d.__get__(obj)
。
而通過訪問對象屬性,自動調(diào)用 descriptor
才是更通用的做法。例如,如果
d
定義了方法
__get__()
,則
obj.d
會調(diào)用
d.__get__(obj)
。
對于對象,
b.x
會被轉(zhuǎn)換成
type(b).__dict__['x'].__get__(b, type(b))
。而對于類(是的,類也可以調(diào)用),
B.x
會被轉(zhuǎn)換成
B.__dict__['x'].__get__(None, B)
。
Descriptor 例子
class
RevealAccess(object):
"""A data descriptor that sets and returns values
normally and prints a message logging their access.
"""
def
__init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def
__get__(self, obj, objtype):
print('Retrieving', self.name)
return self.val
def
__set__(self, obj, val):
print('Updating', self.name)
self.val = val
>>>
class
MyClass(object):
... x = RevealAccess(10, 'var "x"')
... y = 5
...
>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5
來源:lyyyuna
的小花園