五月综合激情婷婷六月,日韩欧美国产一区不卡,他扒开我内裤强吻我下面视频 ,无套内射无矿码免费看黄,天天躁,日日躁,狠狠躁

新聞動(dòng)態(tài)

詳解Python描述符的工作原理

發(fā)布日期:2022-03-13 13:09 | 文章來(lái)源:源碼中國(guó)

一、

其實(shí),在開(kāi)發(fā)過(guò)程中,雖然我們沒(méi)有直接使用到描述符,但是它在底層卻無(wú)時(shí)不刻地被使用到,例如以下這些:

function、bound method、unbound method

裝飾器property、staticmethod、classmethod

是不是都很熟悉?

這些都與描述符有著千絲萬(wàn)縷的關(guān)系,這篇文章我們就來(lái)看一下描述符背后的工作原理。

二、什么是描述符?

在解釋什么是「描述符」之前,我們先來(lái)看一個(gè)簡(jiǎn)單的例子。

這個(gè)例子非常簡(jiǎn)單,我們?cè)陬?lèi)A中定義了一個(gè)類(lèi)屬性x,然后打印它的值。

其實(shí),除了直接定類(lèi)屬性之外,我們還可以這樣定義一個(gè)類(lèi)屬性:

仔細(xì)看,這次類(lèi)屬性x不再是一個(gè)具體的值,而是一個(gè)類(lèi)Ten。Ten中定義了一個(gè)__get__方法,返回具體的值。

在 Python 中,允許把一個(gè)類(lèi)屬性,托管給一個(gè)類(lèi),這個(gè)屬性就是一個(gè)「描述符」。

換句話(huà)說(shuō),「描述符」是一個(gè)「綁定行為」的屬性。

怎么理解這句話(huà)?

回憶一下,我們開(kāi)發(fā)時(shí),一般把「行為」叫做什么?是的,「行為」一般指的是一個(gè)方法。

所以我們也可以把「描述符」理解為:對(duì)象的屬性不再是一個(gè)具體的值,而是交給了一個(gè)方法去定義。

可以想一下,如果我們用一個(gè)方法去定義一個(gè)屬性,這么做的好處是什么?

有了方法,我們就可以在方法內(nèi)實(shí)現(xiàn)自己的邏輯,最簡(jiǎn)單的,我們可以根據(jù)不同的條件,在方法內(nèi)給屬性賦予不同的值,就像下面這樣:

三、描述符協(xié)議

了解了描述符的定義,現(xiàn)在我們把重點(diǎn)放到托管屬性的類(lèi)上。

其實(shí),一個(gè)類(lèi)屬性想要托管給一個(gè)類(lèi),這個(gè)類(lèi)內(nèi)部實(shí)現(xiàn)的方法不能是隨便定義的,它必須遵守「描述符協(xié)議」,也就是要實(shí)現(xiàn)以下幾個(gè)方法:

__get__(self, obj, type=None)
__set__(self, obj, value)
__delete__(self, obj)

只要是實(shí)現(xiàn)了以上幾個(gè)方法的其中一個(gè),那么這個(gè)類(lèi)屬性就可以稱(chēng)作描述符。

另外,描述符又可以分為「數(shù)據(jù)描述符」和「非數(shù)據(jù)描述符」:

只定義了 __get___,叫做非數(shù)據(jù)描述符
除了定義 __get__ 之外,還定義了 __set__ 或 __delete__,叫做數(shù)據(jù)描述符

它們兩者有什么區(qū)別,我會(huì)在下面詳述。

現(xiàn)在我們來(lái)看一個(gè)包含__get____set__方法的描述符例子:

在這例子中,類(lèi)屬性age是一個(gè)描述符,它的值取決于Age類(lèi)。

從輸出結(jié)果來(lái)看,當(dāng)我們獲取或修改age屬性時(shí),調(diào)用了Age__get____set__方法:

  • 當(dāng)調(diào)用p1.age時(shí),__get__被調(diào)用,參數(shù)objPerson實(shí)例,typetype(Person)
  • 當(dāng)調(diào)用Person.age時(shí),__get__被調(diào)用,參數(shù)objNone,typetype(Person)
  • 當(dāng)調(diào)用p1.age = 25時(shí),__set__被調(diào)用,參數(shù)objPerson實(shí)例,value是25
  • 當(dāng)調(diào)用p1.age = -1時(shí),__set__沒(méi)有通過(guò)校驗(yàn),拋出ValueError

其中,調(diào)用__set__傳入的參數(shù),我們比較容易理解,但是對(duì)于__get__方法,通過(guò)類(lèi)或?qū)嵗?/strong>調(diào)用,傳入的參數(shù)是不同的,這是為什么?

這就需要我們了解一下描述符的工作原理。

四、描述符的工作原理

要解釋描述符的工作原理,首先我們需要先從屬性的訪(fǎng)問(wèn)說(shuō)起。

在開(kāi)發(fā)時(shí),不知道你有沒(méi)有想過(guò)這樣一個(gè)問(wèn)題:通常我們寫(xiě)這樣的代碼a.b,其背后到底發(fā)生了什么?

這里的ab可能存在以下情況:

1.a可能是一個(gè)類(lèi),也可能是一個(gè)實(shí)例,我們這里統(tǒng)稱(chēng)為對(duì)象

2.b可能是一個(gè)屬性,也可能是一個(gè)方法,方法其實(shí)也可以看做是類(lèi)的屬性

其實(shí),無(wú)論是以上哪種情況,在 Python 中,都有一個(gè)統(tǒng)一的調(diào)用邏輯:

1.先調(diào)用__getattribute__嘗試獲得結(jié)果

2.如果沒(méi)有結(jié)果,調(diào)用__getattr__

用代碼表示就是下面這樣:

我們這里需要重點(diǎn)關(guān)注一下__getattribute__,因?yàn)樗撬袑傩圆檎业娜肟?,它?nèi)部實(shí)現(xiàn)的屬性查找順序是這樣的:

1.要查找的屬性,在類(lèi)中是否是一個(gè)描述符

2.如果是描述符,再檢查它是否是一個(gè)數(shù)據(jù)描述符

3.如果是數(shù)據(jù)描述符,則調(diào)用數(shù)據(jù)描述符的__get__

4.如果不是數(shù)據(jù)描述符,則從__dict__中查找

5.如果__dict__中查找不到,再看它是否是一個(gè)非數(shù)據(jù)描述符

6.如果是非數(shù)據(jù)描述符,則調(diào)用非數(shù)據(jù)描述符的__get__

7.如果也不是一個(gè)非數(shù)據(jù)描述符,則從類(lèi)屬性中查找

8.如果類(lèi)中也沒(méi)有這個(gè)屬性,拋出AttributeError異常

寫(xiě)成代碼就是下面這樣:

如果不好理解,你最好寫(xiě)一個(gè)程序測(cè)試一下,觀(guān)察各種情況下的屬性的查找順序。

到這里我們可以看到,在一個(gè)對(duì)象中查找一個(gè)屬性,都是先從__getattribute__開(kāi)始的。

__getattribute__中,它會(huì)檢查這個(gè)類(lèi)屬性是否是一個(gè)描述符,如果是一個(gè)描述符,那么就會(huì)調(diào)用它的__get__方法。但具體的調(diào)用細(xì)節(jié)和傳入的參數(shù)是下面這樣的:

如果a是一個(gè)實(shí)例,調(diào)用細(xì)節(jié)為:

所以我們就能看到上面例子輸出的結(jié)果。

五、數(shù)據(jù)描述符和非數(shù)據(jù)描述符

了解了描述符的工作原理,我們繼續(xù)來(lái)看數(shù)據(jù)描述符和非數(shù)據(jù)描述符的區(qū)別。

從定義上來(lái)看,它們的區(qū)別是:

  • 只定義了__get___,叫做非數(shù)據(jù)描述符
  • 除了定義__get__之外,還定義了__set____delete__,叫做數(shù)據(jù)描述符

此外,我們從上面描述符調(diào)用的順序可以看到,在對(duì)象中查找屬性時(shí),數(shù)據(jù)描述符要優(yōu)先于非數(shù)據(jù)描述符調(diào)用。

在之前的例子中,我們定義了__get____set__,所以那些類(lèi)屬性都是數(shù)據(jù)描述符。

我們?cè)賮?lái)看一個(gè)非數(shù)據(jù)描述符的例子:

這段代碼,我們定義了一個(gè)相同名字的屬性和方法foo,如果現(xiàn)在執(zhí)行A().foo,你覺(jué)得會(huì)輸出什么結(jié)果?

答案是abc。

為什么打印的是實(shí)例屬性foo的值,而不是方法foo呢?

這就和非數(shù)據(jù)描述符有關(guān)系了。

我們執(zhí)行dir(A.foo),觀(guān)察結(jié)果:

看到了嗎?Afoo方法其實(shí)實(shí)現(xiàn)了__get__,我們?cè)谏厦娴姆治鲆呀?jīng)得知:只定義__get__方法的對(duì)象,它其實(shí)是一個(gè)非數(shù)據(jù)描述符,也就是說(shuō),我們?cè)陬?lèi)中定義的方法,其實(shí)本身就是一個(gè)非數(shù)據(jù)描述符。

所以,在一個(gè)類(lèi)中,如果存在相同名字的屬性和方法,按照上面所講的__getattribute__中查找屬性的順序,這個(gè)屬性就會(huì)優(yōu)先從實(shí)例中獲取,如果實(shí)例中不存在,才會(huì)從非數(shù)據(jù)描述符中獲取,所以在這里優(yōu)先查找的是實(shí)例屬性foo的值。

到這里我們可以總結(jié)一下關(guān)于描述符的相關(guān)知識(shí)點(diǎn):

  • 描述符必須是一個(gè)類(lèi)屬性
  • __getattribute__是查找一個(gè)屬性(方法)的入口
  • __getattribute__定義了一個(gè)屬性(方法)的查找順序:數(shù)據(jù)描述符、實(shí)例屬性、非數(shù)據(jù)描述符、類(lèi)屬性
  • 如果我們重寫(xiě)了__getattribute__方法,會(huì)阻止描述符的調(diào)用
  • 所有方法其實(shí)都是一個(gè)非數(shù)據(jù)描述符,因?yàn)樗x了__get__

六、描述符的使用場(chǎng)景

了解了描述符的工作原理,那描述符一般用在哪些業(yè)務(wù)場(chǎng)景中呢?

在這里我用描述符實(shí)現(xiàn)了一個(gè)屬性校驗(yàn)器,你可以參考這個(gè)例子,在類(lèi)似的場(chǎng)景中去使用它。

首先我們定義一個(gè)校驗(yàn)基類(lèi)Validator,在__set__方法中先調(diào)用validate方法校驗(yàn)屬性是否符合要求,然后再對(duì)屬性進(jìn)行賦值。

現(xiàn)在,當(dāng)我們對(duì)Person實(shí)例進(jìn)行初始化時(shí),就可以校驗(yàn)這些屬性是否符合預(yù)定義的規(guī)則了。

七、function與method

我們?cè)賮?lái)看一下,在開(kāi)發(fā)時(shí)經(jīng)??吹降?code>function、unbound methodbound method它們之間到底有什么區(qū)別?

來(lái)看下面這段代碼:

從結(jié)果我們可以看出它們的區(qū)別:

  • function準(zhǔn)確來(lái)說(shuō)就是一個(gè)函數(shù),并且它實(shí)現(xiàn)了__get__方法,因此每一個(gè)function都是一個(gè)非數(shù)據(jù)描述符,而在類(lèi)中會(huì)把function放到__dict__中存儲(chǔ)
  • 當(dāng)function被實(shí)例調(diào)用時(shí),它是一個(gè)bound method
  • 當(dāng)function被類(lèi)調(diào)用時(shí), 它是一個(gè)unbound method

function是一個(gè)非數(shù)據(jù)描述符,我們之前已經(jīng)講到了。

bound methodunbound method的區(qū)別就在于調(diào)用方的類(lèi)型是什么,如果是一個(gè)實(shí)例,那么這個(gè)function就是一個(gè)bound method,否則它是一個(gè)unbound method。

八、property/staticmethod/classmethod

我們?cè)賮?lái)看property、staticmethod、classmethod。

這些裝飾器的實(shí)現(xiàn),默認(rèn)是 C 來(lái)實(shí)現(xiàn)的。

其實(shí),我們也可以直接利用 Python 描述符的特性來(lái)實(shí)現(xiàn)這些裝飾器,

property的 Python 版實(shí)現(xiàn):

除此之外,你還可以實(shí)現(xiàn)其他功能強(qiáng)大的裝飾器。

由此可見(jiàn),通過(guò)描述符我們可以實(shí)現(xiàn)強(qiáng)大而靈活的屬性管理功能,對(duì)于一些要求屬性控制比較復(fù)雜的場(chǎng)景,我們可以選擇用描述符來(lái)實(shí)現(xiàn)。

到此這篇關(guān)于詳解Python描述符的工作原理的文章就介紹到這了,更多相關(guān)Python描述符內(nèi)容請(qǐng)搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!

國(guó)外服務(wù)器租用

版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非maisonbaluchon.cn所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。

相關(guān)文章

實(shí)時(shí)開(kāi)通

自選配置、實(shí)時(shí)開(kāi)通

免備案

全球線(xiàn)路精選!

全天候客戶(hù)服務(wù)

7x24全年不間斷在線(xiàn)

專(zhuān)屬顧問(wèn)服務(wù)

1對(duì)1客戶(hù)咨詢(xún)顧問(wèn)

在線(xiàn)
客服

在線(xiàn)客服:7*24小時(shí)在線(xiàn)

客服
熱線(xiàn)

400-630-3752
7*24小時(shí)客服服務(wù)熱線(xiàn)

關(guān)注
微信

關(guān)注官方微信