Python 描述符(Descriptor)入門
很久都沒寫 Flask 代碼相關(guān)了,想想也真是慚愧,然并卵,這次還是不寫 Flask 相關(guān),不服你來打我?。ň瓦@么賤,有本事咬我啊
這次我來寫一下 Python 一個很重要的東西,即 Descriptor (描述符)
初識描述符
老規(guī)矩, Talk is cheap,Show me the code. 我們先來看看一段代碼
classPerson(object):
""""""
#----------------------------------------------------------------------
def__init__(self, first_name, last_name):
"""Constructor"""
self.first_name = first_name
self.last_name = last_name
#----------------------------------------------------------------------
@property
deffull_name(self):
"""
Return the full name
"""
return"%s %s"% (self.first_name, self.last_name)
if__name__=="__main__":
person = Person("Mike","Driscoll")
print(person.full_name)
# 'Mike Driscoll'
print(person.first_name)
# 'Mike'
這段代大家肯定很熟悉,恩, property 嘛,誰不知道呢,但是 property 的實現(xiàn)機制大家清楚么?什么不清楚?那還學(xué)個毛的 Python 啊。。。開個玩笑,我們看下面一段代碼
classProperty(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"
def__init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
ifdocisNoneandfgetisnotNone:
doc = fget.__doc__
self.__doc__ = doc
def__get__(self, obj, objtype=None):
ifobjisNone:
returnself
ifself.fgetisNone:
raiseAttributeError("unreadable attribute")
returnself.fget(obj)
def__set__(self, obj, value):
ifself.fsetisNone:
raiseAttributeError("can't set attribute")
self.fset(obj, value)
def__delete__(self, obj):
ifself.fdelisNone:
raiseAttributeError("can't delete attribute")
self.fdel(obj)
defgetter(self, fget):
returntype(self)(fget, self.fset, self.fdel, self.__doc__)
defsetter(self, fset):
returntype(self)(self.fget, fset, self.fdel, self.__doc__)
defdeleter(self, fdel):
returntype(self)(self.fget, self.fset, fdel, self.__doc__)
看起來是不是很復(fù)雜,沒事,我們來一步步的看。不過這里我們首先給出一個結(jié)論: Descriptors 是一種特殊 的對象,這種對象實現(xiàn)了 __get__ , __set__ , __delete__ 這三個特殊方法。
詳解描述符
說說 Property
在上文,我們給出了 Propery 實現(xiàn)代碼,現(xiàn)在讓我們來詳細說說這個
classPerson(object):
""""""
#----------------------------------------------------------------------
def__init__(self, first_name, last_name):
"""Constructor"""
self.first_name = first_name
self.last_name = last_name
#----------------------------------------------------------------------
@Property
deffull_name(self):
"""
Return the full name
"""
return"%s %s"% (self.first_name, self.last_name)
if__name__=="__main__":
person = Person("Mike","Driscoll")
print(person.full_name)
# 'Mike Driscoll'
print(person.first_name)
# 'Mike'
首先,如果你對裝飾器不了解的話,你可能要去看看這篇文章,簡而言之,在我們正式運行代碼之前,我們的解釋器就會對我們的代碼進行一次掃描,對涉及裝飾器的部分進行替換。類裝飾器同理。在上文中,這段代碼
@Property deffull_name(self): """ Return the full name """ return"%s %s"% (self.first_name, self.last_name)
會觸發(fā)這樣一個過程,即 full_name=Property(full_name) 。然后在我們后面所實例化對象之后我們調(diào)用 person.full_name 這樣一個過程其實等價于 person.full_name.__get__(person) 然后進而觸發(fā) __get__() 方法里所寫的 return self.fget(obj) 即原本上我們所編寫的 def full_name 內(nèi)的執(zhí)行代碼。
這個時候,同志們可以去思考下 getter() , setter() ,以及 deleter() 的具體運行機制了=。=如果還是有問題,歡迎在評論里進行討論。
關(guān)于描述符
還記得之前我們所提到的一個定義么: Descriptors 是一種特殊的對象,這種對象實現(xiàn)了 __get__ , __set__ , __delete__ 這三個特殊方法 。然后在 Python 官方文檔的說明中,為了體現(xiàn)描述符的重要性,有這樣一段話:“They are the mechanism behind properties, methods, static methods, class methods, and super(). They are used throughout Python itself to implement the new style classes introduced in version 2.2. ” 簡而言之就是 先有描述符后有天,秒天秒地秒空氣 。恩,在新式類中,屬性,方法調(diào)用,靜態(tài)方法,類方法等都是基于描述符的特定使用。
OK,你可能想問,為什么描述符是這么重要呢?別急,我們接著看
使用描述符
首先請看下一段代碼
classA(object):#注:在 Python 3.x 版本中,對于 new class 的使用不需要顯式的指定從 object 類進行繼承,如果在 Python 2.X(x>2)的版本中則需要
defa(self): pass if__name__=="__main__": a=A() a.a()
大家都注意到了我們存在著這樣一個語句 a.a() ,好的,現(xiàn)在請大家思考下,我們在調(diào)用這個方法的時候發(fā)生了什么?
OK?想出來了么?沒有?好的我們繼續(xù)
首先我們調(diào)用一個屬性的時候,不管是成員還是方法,我們都會觸發(fā)這樣一個方法用于調(diào)用屬性 __getattribute__() ,在我們的 __getattribute__() 方法中,如果我們嘗試調(diào)用的屬性實現(xiàn)了我們的描述符協(xié)議,那么會產(chǎn)生這樣一個調(diào)用過程 type(a).__dict__['a'].__get__(b,type(b)) 。好的這里我們又要給出一個結(jié)論了:“在這樣一個調(diào)用過程中,有這樣一個優(yōu)先級順序,如果我們所嘗試調(diào)用屬性是一個 data descriptors ,那么不管這個屬性是否存在我們的實例的 __dict__ 字典中,優(yōu)先調(diào)用我們描述符里的 __get__ 方法,如果我們所嘗試調(diào)用屬性是一個 non data descriptors ,那么我們優(yōu)先調(diào)用我們實例里的 __dict__ 里的存在的屬性,如果不存在,則依照相應(yīng)原則往上查找我們類,父類中的 __dict__ 中所包含的屬性,一旦屬性存在,則調(diào)用 __get__ 方法,如果不存在則調(diào)用 __getattr__() 方法”。理解起來有點抽象?沒事,我們馬上會講,不過在這里,我們先要解釋下 data descriptors 與 non data descriptors ,再來看一個例子。什么是 data descriptors 與 non data descriptors 呢?其實很簡單,在描述符中同時實現(xiàn)了 __get__ 與 __set__ 協(xié)議的描述符是 data descriptors ,如果只實現(xiàn)了 __get__ 協(xié)議的則是 non data descriptors 。好了我們現(xiàn)在來看個例子:
importmath
classlazyproperty:
def__init__(self, func):
self.func = func
def__get__(self, instance, owner):
ifinstanceisNone:
returnself
else:
value = self.func(instance)
setattr(instance, self.func.__name__, value)
returnvalue
classCircle:
def__init__(self, radius):
self.radius = radius
pass
@lazyproperty
defarea(self):
print("Com")
returnmath.pi * self.radius *2
deftest(self):
pass
if__name__=='__main__':
c=Circle(4)
print(c.area)
好的,讓我們仔細來看看這段代碼,首先類描述符 @lazyproperty 的替換過程,前面已經(jīng)說了,我們不在重復(fù)。接著,在我們第一次調(diào)用 c.area 的時候,我們首先查詢實例 c 的 __dict__ 中是否存在著 area 描述符,然后發(fā)現(xiàn)在 c 中既不存在描述符,也不存在這樣一個屬性,接著我們向上查詢 Circle 中的 __dict__ ,然后查找到名為 area 的屬性,同時這是一個 non data descriptors ,由于我們的實例字典內(nèi)并不存在 area 屬性,那么我們便調(diào)用類字典中的 area 的 __get__ 方法,并在 __get__ 方法中通過調(diào)用 setattr 方法為實例字典注冊屬性 area 。緊接著,我們在后續(xù)調(diào)用 c.area 的時候,我們能在實例字典中找到 area 屬性的存在,且類字典中的 area 是一個 non data descriptors ,于是我們不會觸發(fā)代碼里所實現(xiàn)的 __get__ 方法,而是直接從實例的字典中直接獲取屬性值。
描述符的使用
描述符的使用面很廣,不過其主要的目的在于讓我們的調(diào)用過程變得可控。因此我們在一些需要對我們調(diào)用過程實行精細控制的時候,使用描述符,比如我們之前提到的這個例子
classlazyproperty:
def__init__(self, func):
self.func = func
def__get__(self, instance, owner):
ifinstanceisNone:
returnself
else:
value = self.func(instance)
setattr(instance, self.func.__name__, value)
returnvalue
def__set__(self, instance, value=0):
pass
importmath
classCircle:
def__init__(self, radius):
self.radius = radius
pass
@lazyproperty
defarea(self, value=0):
print("Com")
ifvalue ==0andself.radius ==0:
raiseTypeError("Something went wring")
returnmath.pi * value *2ifvalue !=0elsemath.pi * self.radius *2
deftest(self):
pass
利用描述符的特性實現(xiàn)懶加載,再比如,我們可以控制屬性賦值的值
classProperty(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"
def__init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
ifdocisNoneandfgetisnotNone:
doc = fget.__doc__
self.__doc__ = doc
def__get__(self, obj, objtype=None):
ifobjisNone:
returnself
ifself.fgetisNone:
raiseAttributeError("unreadable attribute")
returnself.fget(obj)
def__set__(self, obj, value=None):
ifvalueisNone:
raiseTypeError("You can`t to set value as None")
ifself.fsetisNone:
raiseAttributeError("can't set attribute")
self.fset(obj, value)
def__delete__(self, obj):
ifself.fdelisNone:
raiseAttributeError("can't delete attribute")
self.fdel(obj)
defgetter(self, fget):
returntype(self)(fget, self.fset, self.fdel, self.__doc__)
defsetter(self, fset):
returntype(self)(self.fget, fset, self.fdel, self.__doc__)
defdeleter(self, fdel):
returntype(self)(self.fget, self.fset, fdel, self.__doc__)
classtest():
def__init__(self, value):
self.value = value
@Property
defValue(self):
returnself.value
@Value.setter
deftest(self, x):
self.value = x
如上面的例子所描述的一樣,我們可以判斷所傳入的值是否有效等等。
- Python descriptor(描述符)的實現(xiàn)
- Python描述符descriptor使用原理解析
- 通過實例解析python描述符原理作用
- python實現(xiàn)裝飾器、描述符
- Python中屬性和描述符的正確使用
- 詳解Python中的Descriptor描述符類
- Python黑魔法Descriptor描述符的實例解析
- Python中的Descriptor描述符學(xué)習(xí)教程
- Python 的描述符 descriptor詳解
- 解密Python中的描述符(descriptor)
- Python中的類與對象之描述符詳解
- python的描述符(descriptor)、裝飾器(property)造成的一個無限遞歸問題分享
- Python基礎(chǔ)詳解之描述符
相關(guān)文章
Python利用jmespath模塊進行json數(shù)據(jù)處理
jmespath是python的第三方模塊,是需要額外安裝的。它在python原有的json數(shù)據(jù)處理上做出了很大的貢獻。本文將詳細介紹如何利用jmespath實現(xiàn)json數(shù)據(jù)處理,需要的可以參考一下2022-03-03
python django使用haystack:全文檢索的框架(實例講解)
下面小編就為大家?guī)硪黄猵ython django使用haystack:全文檢索的框架(實例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09
python中elasticsearch_dsl模塊的使用方法
這篇文章主要介紹了python中elasticsearch_dsl模塊的使用方法,elasticsearch-dsl是基于elasticsearch-py封裝實現(xiàn)的,提供了更簡便的操作elasticsearch的方法2022-09-09
Python的pandas類庫結(jié)構(gòu)參數(shù)
pandas是基于NumPy 的一種工具,該工具是為了解決數(shù)據(jù)分析任務(wù)而創(chuàng)建的,Pandas 納入了大量庫和一些標(biāo)準(zhǔn)的數(shù)據(jù)模型,提供了高效地操作大型數(shù)據(jù)集所需的工具,需要的朋友可以參考下2023-07-07
python3啟動web服務(wù)引發(fā)的一系列問題匯總
由于行內(nèi)交付的機器已自帶python3 ,沒有采取自行安裝python3,但是運行python腳本時報沒有tornado module,遇到這樣的問題如何處理呢,下面小編給大家介紹下python3啟動web服務(wù)引發(fā)的一系列問題匯總,感興趣的朋友一起看看吧2023-02-02

