首先介紹一下裝飾器,所謂裝飾器,就是接受乙個函式作為引數,然後返回乙個函式的函式。所謂帶引數的裝飾器呢?其實是返回裝飾器的函式。注意啦!python的裝飾器語法是乙個語法糖,實際上並不強制要求你的裝飾器返回函式!你的裝飾器完全返回隨便什麼東西!int、float、bool、str等基本型別,list、tuple、map等集合型別亦或者class都可以!
倒不如說,很多很多python 的裝飾器,其實返回的是class,因為class可以定義很多魔術方法,進而既可以當做函式使用,也可以當做資料使用,很多python的黑科技,都是這個原理。
而在講描述器之前,先要說一下類的三種方法:
1)例項方法,類的例項方法大家都知道,第乙個引數是呼叫該方法的例項。
2)類方法,用@classmethod裝飾的方法,其第乙個引數是呼叫該方法的(例項的)類。
3)靜態方法,用@staticmethod裝飾的方法,其可視為定義在class內部的普通函式。
就像例項方法的第乙個引數是例項,以便對不同勢力該方法的表現不同;類方法的第乙個引數是呼叫其的類,因此如果被定義類方法的子類呼叫了類方法的話,其第乙個引數將是子類,因此可以產生不同的表現。
然後,我們再說類屬性的訪問機制。如果從類出發訪問類的屬性,其實就是在類的__dict__裡查詢該屬性,找不到的話會以此查詢繼承鏈條mro裡後面的各個類的__dict__。而從例項出發訪問屬性的時候呢?又不一樣。所有對例項出發的屬性的方法,都是由類的__getattribute__方法來執行的,它會先查詢例項的__dict__,找不到的話,會查詢其所屬的類的__dict__,然後是繼承鏈條mro裡後面的各個類的__dict__。那麼為啥要用__getattribute__?通過__getattribute__,可以對例項的屬性進行限制。比如說,如果class包含乙個屬性__slots__,這是乙個str的list,那麼對例項定義新的屬性的時候,其屬性就必須包含在__slots__裡。這個確保的途徑,就是__getattribute__做的。__getattribute__還做了一件事,那就是它查詢完所有__dict__之後如果還沒有找到該屬性,而class定義了__getattr__方法的話,__getattribute__就會呼叫__getattr__方法。
很顯然__getattribute__和__getattr__都接受兩個引數,類例項self和字串name。
說到這裡,終於可以說描述器了。
描述器首先必然是乙個class的例項!並且這個class必須定義了__get__方法,以及可選的__set__、__delete__方法。如果定義了__set__方法,該class稱之為資料描述器,否則稱之為非資料描述器。__delete__只在刪除該描述器屬性時被執行。
兩種描述器有啥差別?它們只在從例項訪問屬性時有差別。如果例項的__dict__含有與描述器同名的屬性,那麼根據描述器的不同:__getattribute__會優先返回資料描述器,其次是例項屬性,最後才是非資料描述器。
寫到這裡,終於可以具體講一下描述器了。
__get__方法接受三個引數,self、instance、owner。
__set__方法接受三個引數,self、instance、value。
__delete__方法接受兩個引數,self、instance。
注意了!假設有個類a,其類屬性b是描述器類b的乙個例項,而a是a的乙個例項。
那麼,**「a.b」會呼叫b.__get__(b,a,a);「a.b=***」會呼叫b.__set__(b,a,***);「del(a.b)」和「delattr(a,'b')」會呼叫b.__delete__(b,a)但不一定會刪除class a的b屬性。
而**「a.b」則會呼叫b.__get__(b,none,a);但「a.b=***」不會呼叫b.__set__(b,none,***),而直接修改a的__dict__;「del(a.b)」和「delattr(a,'b')」也不會呼叫b.__delete__(b,none)而是直接從a的__dict__中刪除'b'鍵。
這樣一看,大致就清楚了。描述器的例項是不會單獨使用的,它總是作為類的屬性(而不是例項的屬性)存在。當我們從例項訪問乙個描述器屬性的時候,其實我們是呼叫了描述器自己的方法,而這個方法又可以獲得我們訪問描述器屬性的例項作為引數————因此這個方法可以偽裝成例項方法!
是的,這就是關鍵啦。描述器的例項,明明是乙個類屬性,卻可以偽裝成例項屬性!
end。
回頭看看staticmethod和classmethod,就更有意思了。這倆玩意都是裝飾器,但是其實它們是偽裝成裝飾器的類!它們的__init__方法可以接受函式,返回乙個例項,因此用這倆來裝飾乙個例項方法的話,例項方法實際上被替換成了乙個類例項。而它們本身,卻又是描述器。也就是說,例項方法變成了乙個描述器例項。而這個描述器例項的__get__方法,又會返回乙個函式/方法,結果是例項方法雖然被改變成了描述器例項,但實際上卻仍舊可以當成乙個函式來訪問!而property其實也是乙個偽裝成裝飾器的類,同時也是描述器;而且它還有三個例項方法又可以作為裝飾器使用…………
python黑科技啊!佩服佩服!!
python 描述 python描述符
在python中,訪問乙個屬性的優先順序順序按照如下順序 1.類屬性2.資料描述符3.例項屬性4.非資料描述符5.getattr 方法。描述符,用一句話來說,就是將某種特殊型別的類的例項指派給另乙個類的屬性 注意 這裡是類屬性,而不是物件屬性 而這種特殊型別的類就是實現了 get set delet...
python 描述 描述Python類屬性
def init self,name,info self.name name self.info info ls user 李四 print ls.info 執行結果 如果想獲得male屬性,則需要用到 getattr 魔法方法。class user object def init self,nam...
python類屬性描述 描述Python類屬性
ls user 李四 print ls.info 執行結果 如果想獲得male屬性,則需要用到 getattr 魔法方法。class user object def init self,name,info self.name name self.info info def getattr self,...