導語:本文章記錄了本人在學習Python基礎之元編程篇的重點知識及我的心得,打算入門Python的朋友們能夠來一塊兒學習並交流。
本文重點:python
一、瞭解描述符的定義,功能,協議和用法;
二、瞭解覆蓋型描述符和非覆蓋型描述符的概念和區別;
三、瞭解描述符的用法建議。
描述符:是實現了特定協議的類。
描述符功能:是對多個屬性運用相同存取邏輯的一種方式。
描述符協議:包括__get__、__set__、和__delete__方法。一般只實現部分協議。大多數描述符只實現了__get__和__set__方法,可是property類實現了完整的描述符協議。
編程
下面咱們用描述符來實現Python中的動態屬性和特性中說起的訂單結算代碼:segmentfault
第四版:使用描述符實現訂單結算功能緩存
class Quantity:#描述符基於協議實現,無需建立子類。 def __init__(self,storage_name): self.storage_name=storage_name#storage_name是託管實例中存儲值的屬性的名稱。 def __set__(self, instance, value):#重要!instance是LineItem實例,self是描述符實例。 if value > 0: instance.__dict__[self.storage_name]=value#此處必須直接存入__dict__,不然使用setattr函數會致使無限遞歸。 else: raise ValueError('Value must be > 0') class LineItem: weight = Quantity('weight')#將描述符實例綁定到weight屬性。 price = Quantity('price')#同上。 def __init__(self,description,weight,price): self.description=description self.weight=weight self.price=price def subtotal(self): return self.weight*self.price
小結:描述符類的實例能用做託管類的屬性
,這一點很重要!
不過在上文中的託管類定義體中,實例化描述符若是能按照weight = Quantity()這種格式聲明就更好了。爲此咱們須要寫一版自動獲取存取屬性名稱的代碼。函數
第五版:改進描述符類——自動獲取存取屬性名稱工具
class Quantity:#改進版描述符類 __counter = 0 def __init__(self): cls = self.__class__ prefix = cls.__name__ index = cls.__counter self.storage_name = '_{}#{}'.format(prefix, index) #每一個描述符實例的屬性名稱都是獨一無二的。 cls.__counter += 1 def __get__(self, instance, owner): #此處owner參數是託管類LineItem。 return getattr(instance, self.storage_name) #從instance中獲取儲存屬性的值。 def __set__(self, instance, value): if value > 0: setattr(instance, self.storage_name, value) #使用setattr把值儲存在instance中。 else: raise ValueError('value must be > 0') class LineItem:#託管類 weight = Quantity() price = Quantity() def __init__(self,description,weight,price): self.description=description self.weight=weight self.price=price def subtotal(self): return self.weight*self.price
Tips:一般,咱們不會在使用描述符的模塊中定義描述符,而是在一個單獨的實用工具模塊中定義,以便在整個應用中使用。學習
咱們在Python中的動態屬性和特性中提到過,抽象定義特性的方式有兩種,一是使用特性工廠函數,二是使用描述符類。
如今來對兩種方式的優勢進行對比辨析:code
描述符類:模式可拓展。可經過子類共享代碼,構建具備部分相同功能的專用描述符,應用更普遍。
我的建議:當兩種模式均能實現目標時,推薦使用描述類。
orm
覆蓋型描述符:實現__set__方法的描述符屬於覆蓋型描述符。
特性是覆蓋型描述符。
非覆蓋型描述符:沒有實現__set__方法的描述符屬於非覆蓋型描述符。
類中定義的方法是非覆蓋型描述符。
小結:若是設置了同名實例屬性,對於非覆蓋型描述符而言會被覆蓋;對於沒有實現__get__方法的覆蓋型描述符而言,在讀操做時描述符對象也會被覆蓋。對象
使用特性以保持簡單
:建立只讀屬性最簡單的方式是使用特性。只讀描述符必須有__set__方法
:只讀描述符必須定義__get__和__set__兩個方法,只讀屬性的__set__方法只需拋出AttributeError異常,並提供合適的錯誤信息。用於驗證的描述符能夠只有__set__方法
:描述符用於驗證屬性時能夠不實現__get__,這樣從實例中讀取同名屬性的速度很快。僅有__get__方法的描述符能夠實現高效緩存。
非特殊的方法能夠被實例屬性覆蓋。