通過上面的講解,咱們已經知道如何定義描述符,且明白了描述符是如何工做的。python
正常人所見過的描述符的用法就是上篇文章提到的那些,我想說的是那只是描述符協議最多見的應用之一,或許你還不知道,其實有不少 Python 的特性的底層實現機制都是基於 描述符協議
的,好比咱們熟悉的@property
、@classmethod
、@staticmethod
和 super
等。微信
先來講說 property
吧。函數
有了第一篇的基礎,咱們知道了 property 的基本用法。這裏我直接切入主題,從第一篇的例子裏精簡了一下。code
class Student: def __init__(self, name): self.name = name @property def math(self): return self._math @math.setter def math(self, value): if 0 <= value <= 100: self._math = value else: raise ValueError("Valid value must be in [0, 100]")
不防再簡單回顧一下它的用法,經過property裝飾的函數,如例子中的 math 會變成 Student 實例的屬性。而對 math 屬性賦值會進入 使用 math.setter
裝飾函數的邏輯代碼塊。blog
爲何說 property 底層是基於描述符協議的呢?經過 PyCharm 點擊進入 property 的源碼,很惋惜,只是一份相似文檔同樣的僞源碼,並無其具體的實現邏輯。文檔
不過,從這份僞源碼的魔法函數結構組成,能夠大致知道其實現邏輯。get
這裏我本身經過模仿其函數結構,結合「描述符協議」來本身實現類 property
特性。源碼
代碼以下:it
class TestProperty(object): def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel self.__doc__ = doc def __get__(self, obj, objtype=None): print("in __get__") if obj is None: return self if self.fget is None: raise AttributeError return self.fget(obj) def __set__(self, obj, value): print("in __set__") if self.fset is None: raise AttributeError self.fset(obj, value) def __delete__(self, obj): print("in __delete__") if self.fdel is None: raise AttributeError self.fdel(obj) def getter(self, fget): print("in getter") return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): print("in setter") return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): print("in deleter") return type(self)(self.fget, self.fset, fdel, self.__doc__)
而後 Student 類,咱們也相應改爲以下class
class Student: def __init__(self, name): self.name = name # 其實只有這裏改變 @TestProperty def math(self): return self._math @math.setter def math(self, value): if 0 <= value <= 100: self._math = value else: raise ValueError("Valid value must be in [0, 100]")
爲了儘可能讓你少產生一點疑惑,我這裏作兩點說明:
使用TestProperty
裝飾後,math
再也不是一個函數,而是TestProperty
類的一個實例。因此第二個math函數可使用 math.setter
來裝飾,本質是調用TestProperty.setter
來產生一個新的 TestProperty
實例賦值給第二個math
。
第一個 math
和第二個 math
是兩個不一樣 TestProperty
實例。但他們都屬於同一個描述符類(TestProperty),當對 math 對於賦值時,就會進入 TestProperty.__set__
,當對math 進行取值裏,就會進入 TestProperty.__get__
。仔細一看,其實最終訪問的仍是Student實例的 _math
屬性。
說了這麼多,仍是運行一下,更加直觀一點。
# 運行後,會直接打印這一行,這是在實例化 TestProperty 並賦值給第二個math in setter >>> >>> s1.math = 90 in __set__ >>> s1.math in __get__ 90
對於以上理解 property
的運行原理有困難的同窗,請務必參照我上面寫的兩點說明。若有其餘疑問,能夠加微信與我進行探討。
說完了 property
,這裏再來說講 @classmethod
和 @staticmethod
的實現原理。
我這裏定義了一個類,用了兩種方式來實現靜態方法。
class Test: @staticmethod def myfunc(): print("hello") # 上下兩種寫法等價 class Test: def myfunc(): print("hello") # 重點:這就是描述符的體現 myfunc = staticmethod(myfunc)
這兩種寫法是等價的,就好像在 property
同樣,其實如下兩種寫法也是等價的。
@TestProperty def math(self): return self._math math = TestProperty(fget=math)
話題仍是轉回到 staticmethod
這邊來吧。
由上面的註釋,能夠看出 staticmethod
其實就至關於一個描述符類,而myfunc
在此刻變成了一個描述符。關於 staticmethod
的實現,你能夠參照下面這段我本身寫的代碼,加以理解。
調用這個方法能夠知道,每調用一次,它都會通過描述符類的 __get__
。
>>> Test.myfunc() in staticmethod __get__ hello >>> Test().myfunc() in staticmethod __get__ hello
一樣的 classmethod
也是同樣。
class classmethod(object): def __init__(self, f): self.f = f def __get__(self, instance, owner=None): print("in classmethod __get__") def newfunc(*args): return self.f(owner, *args) return newfunc class Test: def myfunc(cls): print("hello") # 重點:這就是描述符的體現 myfunc = classmethod(myfunc)
驗證結果以下
>>> Test.myfunc() in classmethod __get__ hello >>> Test().myfunc() in classmethod __get__ hello
講完了 property
、staticmethod
和classmethod
與 描述符的關係。我想你應該對描述符在 Python 中的應用有了更深的理解。對於 super 的實現原理,就交由你來本身完成。