欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python 描述符(Descriptor)入門

 更新時(shí)間:2016年11月20日 10:18:38   作者:Manjusaka  
本文給大家介紹的是Python中比較重要的一個(gè)知識(shí)點(diǎn)--描述符(Descriptor),描述符(descriptor)是Python語言核心中困擾我時(shí)間最長的一個(gè)特性,但是一旦你理解了之后,描述符的確還是有它的應(yīng)用價(jià)值的。

很久都沒寫 Flask 代碼相關(guān)了,想想也真是慚愧,然并卵,這次還是不寫 Flask 相關(guān),不服你來打我啊(就這么賤,有本事咬我啊

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

初識(shí)描述符

老規(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 的實(shí)現(xiàn)機(jī)制大家清楚么?什么不清楚?那還學(xué)個(gè)毛的 Python 啊。。。開個(gè)玩笑,我們看下面一段代碼

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ù)雜,沒事,我們來一步步的看。不過這里我們首先給出一個(gè)結(jié)論: Descriptors 是一種特殊 的對(duì)象,這種對(duì)象實(shí)現(xiàn)了 __get__ , __set__ , __delete__ 這三個(gè)特殊方法。

詳解描述符

說說 Property

在上文,我們給出了 Propery 實(shí)現(xiàn)代碼,現(xiàn)在讓我們來詳細(xì)說說這個(gè)

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'

首先,如果你對(duì)裝飾器不了解的話,你可能要去看看這篇文章,簡而言之,在我們正式運(yùn)行代碼之前,我們的解釋器就會(huì)對(duì)我們的代碼進(jìn)行一次掃描,對(duì)涉及裝飾器的部分進(jìn)行替換。類裝飾器同理。在上文中,這段代碼

@Property
deffull_name(self):
"""
 Return the full name
 """
return"%s %s"% (self.first_name, self.last_name)

會(huì)觸發(fā)這樣一個(gè)過程,即 full_name=Property(full_name) 。然后在我們后面所實(shí)例化對(duì)象之后我們調(diào)用 person.full_name 這樣一個(gè)過程其實(shí)等價(jià)于 person.full_name.__get__(person) 然后進(jìn)而觸發(fā) __get__() 方法里所寫的 return self.fget(obj) 即原本上我們所編寫的 def full_name 內(nèi)的執(zhí)行代碼。

這個(gè)時(shí)候,同志們可以去思考下 getter() , setter() ,以及 deleter() 的具體運(yùn)行機(jī)制了=。=如果還是有問題,歡迎在評(píng)論里進(jìn)行討論。

關(guān)于描述符

還記得之前我們所提到的一個(gè)定義么: Descriptors 是一種特殊的對(duì)象,這種對(duì)象實(shí)現(xiàn)了 __get__ , __set__ , __delete__ 這三個(gè)特殊方法 。然后在 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,你可能想問,為什么描述符是這么重要呢?別急,我們接著看

使用描述符

首先請(qǐng)看下一段代碼

classA(object):#注:在 Python 3.x 版本中,對(duì)于 new class 的使用不需要顯式的指定從 object 類進(jìn)行繼承,如果在 Python 2.X(x>2)的版本中則需要

defa(self):
pass
if__name__=="__main__":
 a=A()
 a.a()

大家都注意到了我們存在著這樣一個(gè)語句 a.a() ,好的,現(xiàn)在請(qǐng)大家思考下,我們?cè)谡{(diào)用這個(gè)方法的時(shí)候發(fā)生了什么?

OK?想出來了么?沒有?好的我們繼續(xù)

首先我們調(diào)用一個(gè)屬性的時(shí)候,不管是成員還是方法,我們都會(huì)觸發(fā)這樣一個(gè)方法用于調(diào)用屬性 __getattribute__() ,在我們的 __getattribute__() 方法中,如果我們嘗試調(diào)用的屬性實(shí)現(xiàn)了我們的描述符協(xié)議,那么會(huì)產(chǎn)生這樣一個(gè)調(diào)用過程 type(a).__dict__['a'].__get__(b,type(b)) 。好的這里我們又要給出一個(gè)結(jié)論了:“在這樣一個(gè)調(diào)用過程中,有這樣一個(gè)優(yōu)先級(jí)順序,如果我們所嘗試調(diào)用屬性是一個(gè) data descriptors ,那么不管這個(gè)屬性是否存在我們的實(shí)例的 __dict__ 字典中,優(yōu)先調(diào)用我們描述符里的 __get__ 方法,如果我們所嘗試調(diào)用屬性是一個(gè) non data descriptors ,那么我們優(yōu)先調(diào)用我們實(shí)例里的 __dict__ 里的存在的屬性,如果不存在,則依照相應(yīng)原則往上查找我們類,父類中的 __dict__ 中所包含的屬性,一旦屬性存在,則調(diào)用 __get__ 方法,如果不存在則調(diào)用 __getattr__() 方法”。理解起來有點(diǎn)抽象?沒事,我們馬上會(huì)講,不過在這里,我們先要解釋下 data descriptors 與 non data descriptors ,再來看一個(gè)例子。什么是 data descriptors 與 non data descriptors 呢?其實(shí)很簡單,在描述符中同時(shí)實(shí)現(xiàn)了 __get__ 與 __set__ 協(xié)議的描述符是 data descriptors ,如果只實(shí)現(xiàn)了 __get__ 協(xié)議的則是 non data descriptors 。好了我們現(xiàn)在來看個(gè)例子:

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)

好的,讓我們仔細(xì)來看看這段代碼,首先類描述符 @lazyproperty 的替換過程,前面已經(jīng)說了,我們不在重復(fù)。接著,在我們第一次調(diào)用 c.area 的時(shí)候,我們首先查詢實(shí)例 c 的 __dict__ 中是否存在著 area 描述符,然后發(fā)現(xiàn)在 c 中既不存在描述符,也不存在這樣一個(gè)屬性,接著我們向上查詢 Circle 中的 __dict__ ,然后查找到名為 area 的屬性,同時(shí)這是一個(gè) non data descriptors ,由于我們的實(shí)例字典內(nèi)并不存在 area 屬性,那么我們便調(diào)用類字典中的 area 的 __get__ 方法,并在 __get__ 方法中通過調(diào)用 setattr 方法為實(shí)例字典注冊(cè)屬性 area 。緊接著,我們?cè)诤罄m(xù)調(diào)用 c.area 的時(shí)候,我們能在實(shí)例字典中找到 area 屬性的存在,且類字典中的 area 是一個(gè) non data descriptors ,于是我們不會(huì)觸發(fā)代碼里所實(shí)現(xiàn)的 __get__ 方法,而是直接從實(shí)例的字典中直接獲取屬性值。

描述符的使用

描述符的使用面很廣,不過其主要的目的在于讓我們的調(diào)用過程變得可控。因此我們?cè)谝恍┬枰獙?duì)我們調(diào)用過程實(shí)行精細(xì)控制的時(shí)候,使用描述符,比如我們之前提到的這個(gè)例子

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

利用描述符的特性實(shí)現(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

如上面的例子所描述的一樣,我們可以判斷所傳入的值是否有效等等。

相關(guān)文章

  • pycharm運(yùn)行pytest中文編碼格式錯(cuò)亂解決

    pycharm運(yùn)行pytest中文編碼格式錯(cuò)亂解決

    這篇文章主要為大家介紹了pycharm運(yùn)行pytest中文編碼格式錯(cuò)亂的解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • Python getsizeof()和getsize()區(qū)分詳解

    Python getsizeof()和getsize()區(qū)分詳解

    這篇文章主要介紹了Python getsizeof()和getsize()區(qū)分詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 使用Python對(duì)Dicom文件進(jìn)行讀取與寫入的實(shí)現(xiàn)

    使用Python對(duì)Dicom文件進(jìn)行讀取與寫入的實(shí)現(xiàn)

    這篇文章主要介紹了使用Python對(duì)Dicom文件進(jìn)行讀取與寫入的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • python爬取哈爾濱天氣信息

    python爬取哈爾濱天氣信息

    這篇文章主要為大家詳細(xì)介紹了python爬蟲抓取哈爾濱天氣信息,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Python中三種花式打印的示例詳解

    Python中三種花式打印的示例詳解

    在Python中有很多好玩的花式打印,我們今天就來挑戰(zhàn)下面三個(gè)常見的花式打印。文中的示例代碼講解詳細(xì),感興趣的小伙伴快跟隨小編一起學(xué)習(xí)一下吧
    2022-03-03
  • 如何利用Playwright庫進(jìn)行電影網(wǎng)站數(shù)據(jù)的獲取

    如何利用Playwright庫進(jìn)行電影網(wǎng)站數(shù)據(jù)的獲取

    playwright庫是微軟開源的一個(gè)庫,這個(gè)庫的功能更加的強(qiáng)大,除了可以實(shí)現(xiàn)同步操作,同樣也可以實(shí)現(xiàn)異步的操作,這篇文章主要介紹了如何利用Playwright庫進(jìn)行電影網(wǎng)站數(shù)據(jù)的獲取,需要的朋友可以參考下
    2023-05-05
  • python生成帶有表格的圖片實(shí)例

    python生成帶有表格的圖片實(shí)例

    今天小編就為大家分享一篇python生成帶有表格的圖片實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-02-02
  • python scipy求解非線性方程的方法(fsolve/root)

    python scipy求解非線性方程的方法(fsolve/root)

    今天小編就為大家分享一篇python scipy求解非線性方程的方法(fsolve/root),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-11-11
  • 為什么python比較流行

    為什么python比較流行

    在本篇文章里小編給大家分析了關(guān)于python比較流行的原因以及優(yōu)勢等,需要的朋友們可以參考下。
    2020-06-06
  • python selenium自動(dòng)上傳有贊單號(hào)的操作方法

    python selenium自動(dòng)上傳有贊單號(hào)的操作方法

    這篇文章主要介紹了python selenium自動(dòng)上傳有贊單號(hào)的操作代碼,代碼簡單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-07-07

最新評(píng)論