搜尋
首頁後端開發Python教學Python 描述符(Descriptor)入門

很久都沒寫Flask 程式碼相關了,想想也真是慚愧,然並卵,這次還是不寫Flask 相關,不服你來打我啊(就這麼賤,有本事咬我啊

這次我來寫一下Python 一個很重要的東西,即Descriptor (描述詞)

初識描述符

老規矩, 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 的實現機制大家清楚麼? 啊。 。 。開個玩笑,我們看下面一段程式碼

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__)

   

看起來是不是很複雜,沒事,我們來一步步的看。不過這裡我們先給一個結論: Descriptors 是一種特殊 的對象,這種對象實作了 __get__ , __set__ , __delete__ 這三個特殊方法。

詳解描述符

說說Property

在上文,我們給出了Propery 實現代碼,現在讓我們來詳細說說這個

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)

會觸發這樣一個過程,即 full_name=Property(full_name) 。然後在我們後面所實例化物件之後我們調用 person.full_name 這樣一個過程其實等價於 person.full_name.__get__(person) 然後再觸發 __get__() 方法裡所寫的 return self.fget(obj) 即原本上我們所寫的 def full_name 內的執行程式碼。

這時候,同志們可以去思考下 getter() , setter() ,以及 deleter() 的具體運轉機制了=。 =如果還是有問題,歡迎在評論裡討論。

關於描述符

還記得之前我們所提到的一個定義麼: Descriptors 是一種特殊的對象,這種對象實現了 __get__ , __set__ , __delete__ 這三個特殊方法 。然後在 Python 官方文件的說明中,為了體現描述符的重要性,有這樣一段話:「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. 」 簡而言之就是 先有描述詞後有天,秒天秒地秒空氣 。恩,在新式類別中,屬性,方法調用,靜態方法,類別方法等都是基於描述符的特定使用。

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() ,好的,現在請大家思考下,我們在調用這個方法的時候發生了什麼事?

OK?想出來了麼?沒有?好的我們繼續

首先我們調用一個屬性的時候,不管是成員還是方法,我們都會觸發這樣一個方法用於調用屬性 __getattribute__() ,在我們的 __getattribute__() 方法中,如果我們嘗試呼叫的屬性實作了我們的描述符協議,那麼會產生這樣一個呼叫過程 type(a).__dict__['a'].__get__(b,type(b)) 。好的這裡我們又要給一個結論了:「在這樣一個呼叫過程中,有這樣一個優先順序,如果我們嘗試呼叫屬性是一個 data descriptors ,那麼不管這個屬性是否存在我們的實例的 __dict__ 字典中,優先調用我們描述符裡的 __get__ 方法,如果我們嘗試調用屬性是一個 non data descriptors ,那麼我們優先調用我們實例裡的 __dict__ 裡的存在的屬性,如果不存在,則按照相應原則往上查找我們類,父類中的 __dict__ 中所包含的屬性,一旦屬性存在,則呼叫 __get__ 方法,如果不存在則呼叫 __getattr__() 方法」。理解起來有點抽象?沒事,我們馬上會講,不過在這裡,我們先解釋下 data descriptors 與 non data descriptors ,再來看一個例子。什麼是 data descriptors 與 non data descriptors 呢?其實很簡單,在描述符中同時實作了 __get__ 與 __set__ 協定的描述符是 data descriptors ,如果只實作了 __get__ 協定的則是 non data descriptors 。好了我們現在來看個範例:

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 的替换过程,前面已经说了,我们不在重复。接着,在我们第一次调用 c.area 的时候,我们首先查询实例 c 的 __dict__ 中是否存在着 area 描述符,然后发现在 c 中既不存在描述符,也不存在这样一个属性,接着我们向上查询 Circle 中的 __dict__ ,然后查找到名为 area 的属性,同时这是一个 non data descriptors ,由于我们的实例字典内并不存在 area 属性,那么我们便调用类字典中的 area 的 __get__ 方法,并在 __get__ 方法中通过调用 setattr 方法为实例字典注册属性 area 。紧接着,我们在后续调用 c.area 的时候,我们能在实例字典中找到 area 属性的存在,且类字典中的 area 是一个 non data descriptors ,于是我们不会触发代码里所实现的 __get__ 方法,而是直接从实例的字典中直接获取属性值。

描述符的使用

描述符的使用面很广,不过其主要的目的在于让我们的调用过程变得可控。因此我们在一些需要对我们调用过程实行精细控制的时候,使用描述符,比如我们之前提到的这个例子

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

   

利用描述符的特性实现懒加载,再比如,我们可以控制属性赋值的值

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)入门,更多相关文章请关注PHP中文网(www.php.cn)!


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python vs.C:申請和用例Python vs.C:申請和用例Apr 12, 2025 am 12:01 AM

Python适合数据科学、Web开发和自动化任务,而C 适用于系统编程、游戏开发和嵌入式系统。Python以简洁和强大的生态系统著称,C 则以高性能和底层控制能力闻名。

2小時的Python計劃:一種現實的方法2小時的Python計劃:一種現實的方法Apr 11, 2025 am 12:04 AM

2小時內可以學會Python的基本編程概念和技能。 1.學習變量和數據類型,2.掌握控制流(條件語句和循環),3.理解函數的定義和使用,4.通過簡單示例和代碼片段快速上手Python編程。

Python:探索其主要應用程序Python:探索其主要應用程序Apr 10, 2025 am 09:41 AM

Python在web開發、數據科學、機器學習、自動化和腳本編寫等領域有廣泛應用。 1)在web開發中,Django和Flask框架簡化了開發過程。 2)數據科學和機器學習領域,NumPy、Pandas、Scikit-learn和TensorFlow庫提供了強大支持。 3)自動化和腳本編寫方面,Python適用於自動化測試和系統管理等任務。

您可以在2小時內學到多少python?您可以在2小時內學到多少python?Apr 09, 2025 pm 04:33 PM

兩小時內可以學到Python的基礎知識。 1.學習變量和數據類型,2.掌握控制結構如if語句和循環,3.了解函數的定義和使用。這些將幫助你開始編寫簡單的Python程序。

如何在10小時內通過項目和問題驅動的方式教計算機小白編程基礎?如何在10小時內通過項目和問題驅動的方式教計算機小白編程基礎?Apr 02, 2025 am 07:18 AM

如何在10小時內教計算機小白編程基礎?如果你只有10個小時來教計算機小白一些編程知識,你會選擇教些什麼�...

如何在使用 Fiddler Everywhere 進行中間人讀取時避免被瀏覽器檢測到?如何在使用 Fiddler Everywhere 進行中間人讀取時避免被瀏覽器檢測到?Apr 02, 2025 am 07:15 AM

使用FiddlerEverywhere進行中間人讀取時如何避免被檢測到當你使用FiddlerEverywhere...

Python 3.6加載Pickle文件報錯"__builtin__"模塊未找到怎麼辦?Python 3.6加載Pickle文件報錯"__builtin__"模塊未找到怎麼辦?Apr 02, 2025 am 07:12 AM

Python3.6環境下加載Pickle文件報錯:ModuleNotFoundError:Nomodulenamed...

如何提高jieba分詞在景區評論分析中的準確性?如何提高jieba分詞在景區評論分析中的準確性?Apr 02, 2025 am 07:09 AM

如何解決jieba分詞在景區評論分析中的問題?當我們在進行景區評論分析時,往往會使用jieba分詞工具來處理文�...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。