Python神奇方法是指一些允許在自定義類中增加“神奇”功能的方法。而在Python官方文檔中,有關(guān)這些方法的介紹或描述不僅內(nèi)容分散,而且組織結(jié)構(gòu)也相對(duì)松散。本文便對(duì)Python神奇方法做了系統(tǒng)的梳理。對(duì)于初學(xué)者亦或Python行家,都或多或少的會(huì)有些幫助。
話不多說(shuō),直接進(jìn)入主題!
Python神奇指南目錄
簡(jiǎn)介
搭建與初始化
在自定義類中運(yùn)用操作符
神奇方法比較
神奇方法數(shù)字
描述自定義類
控制屬性訪問(wèn)
制作自定義序列
反射
可調(diào)用對(duì)象
上下文管理器
構(gòu)建描述符對(duì)象
簡(jiǎn)介
何為神奇方法呢?它們是面向Python中的一切,是一些特殊的方法允許在自己的定義類中定義增加“神奇”的功能。它們總是使用雙下劃線(比如__init__或__lt__),但它們的文檔沒(méi)有很好地把它們表現(xiàn)出來(lái)。所有這些神奇方法都出現(xiàn)在Python的官方文檔中,但內(nèi)容相對(duì)分散,組織結(jié)構(gòu)也顯得松散。還有你會(huì)難以發(fā)現(xiàn)一個(gè)實(shí)例(雖然他們被設(shè)計(jì)很棒,在語(yǔ)言參考中被詳細(xì)描述,可之后就會(huì)伴隨著枯燥的語(yǔ)法描述等)。
為了彌補(bǔ)Python官方文檔的這些缺陷,作者整理了這篇有關(guān)magic method的文章,旨在用作教程、復(fù)習(xí)或參考文檔。
搭建與初始化
相信大家都熟悉這個(gè)最基礎(chǔ)的神奇方法__init__。它令你能自定義一個(gè)對(duì)象的初始化行為。而當(dāng)我調(diào)用x=SomeClass()時(shí),__init__并不是最先被調(diào)用的。實(shí)際上有一個(gè)叫做__new__的方法,事實(shí)上是它創(chuàng)建了實(shí)例,它傳遞任何參數(shù)給初始化程序來(lái)達(dá)到創(chuàng)建的目的。在對(duì)象生命周期結(jié)束時(shí),調(diào)用__del__。讓我們更近地觀察下這3個(gè)神奇方法吧:
__new__(cls,[…)
一個(gè)對(duì)象的實(shí)例化時(shí)__new__是第一個(gè)被調(diào)用的方法。在類中傳遞其他任何參數(shù)到__init__。__new__很少被使用,這樣做確實(shí)有其目的,特別是當(dāng)一個(gè)子類繼承一個(gè)不可改變的類型(一個(gè)元組或一個(gè)字符串)時(shí)。
__init__(self,[…)
類的初始化。創(chuàng)建對(duì)象后,python解釋器默認(rèn)調(diào)用__init__()方法。無(wú)論主構(gòu)造函數(shù)調(diào)用什么,它都會(huì)被傳遞。__init__幾乎在Python類定義中普遍使用。
__del__(self)
如果__new__和__init__構(gòu)成了對(duì)象的構(gòu)造函數(shù),__ del__就是析構(gòu)函數(shù)。當(dāng)刪除一個(gè)對(duì)象時(shí),python解釋器也會(huì)默認(rèn)調(diào)用__del__()方法。在python中,對(duì)于開(kāi)發(fā)者來(lái)說(shuō)很少會(huì)直接銷毀對(duì)象(如果需要,應(yīng)該使用del關(guān)鍵字銷毀)。Python的內(nèi)存管理機(jī)制能夠很好的勝任這份工作。也就是說(shuō),不管是手動(dòng)調(diào)用del還是由python自動(dòng)回收都會(huì)觸發(fā)__del__方法執(zhí)行。
如下,是是__init__和__del__的例子:
fromos.pathimportjoinclassFileObject:'''對(duì)文件對(duì)象的包裝,確保文件在關(guān)閉時(shí)得到刪除'''def__init__(self,filepath='~',filename='sample.txt'):#按filepath,讀寫(xiě)模式打開(kāi)名為filename的文件self.file=open(join(filepath,filename),'r+')def__del__(self):self.file.close()delself.file
在自定義類中運(yùn)用操作符
神奇方法比較:
Python有一大堆magic method,旨在使用運(yùn)算符實(shí)現(xiàn)對(duì)象之間的直觀比較,而非別扭的方法調(diào)用。它們還提供了一種方法來(lái)覆蓋用于對(duì)象比較的默認(rèn)Python行為。下面是這些方法的列表以及它們的作用:
__cmp__(self,other)
__cmp__是神奇方法中最基礎(chǔ)的一個(gè)。實(shí)際上它實(shí)現(xiàn)所有比較操作符行為(<,==,!=,等),但它有可能不按你想要的方法工作(例如,一個(gè)實(shí)例是否等于另一個(gè)這取決于比較的準(zhǔn)則,以及一個(gè)實(shí)例是否大于其他的這也取決于其他的準(zhǔn)則)。如果self < other,那__cmp__應(yīng)當(dāng)返回一個(gè)負(fù)整數(shù);如果self == other,則返回0;如果self > other,則返回正整數(shù)。它通常是最好的定義,而不需要你一次就全定義好它們,但當(dāng)你需要用類似的準(zhǔn)則進(jìn)行所有的比較時(shí),__cmp__會(huì)是一個(gè)很好的方式,幫你節(jié)省重復(fù)性和提高明確度。
__eq__(self,other)
定義了相等操作符,==的行為。
__ne__(self,other)
定義了不相等操作符,!=的行為。
__lt__(self,other)
定義了小于操作符,<的行為。
__gt__(self,other)
定義了大于操作符,>的行為。
__le__(self,other)
定義了小于等于操作符,<=的行為。
__ge__(self,other)
定義了大于等于操作符,>=的行為。
舉一個(gè)例子,設(shè)想對(duì)單詞進(jìn)行類定義。我們可能希望按照內(nèi)部對(duì)字符串的默認(rèn)比較行為,即字典序(通過(guò)字母)來(lái)比較單詞,也希望能夠基于某些其他的準(zhǔn)則,像是長(zhǎng)度或音節(jié)數(shù)。在本例中,我們通過(guò)單詞長(zhǎng)度排序,以下給出實(shí)現(xiàn):
classWord(str):'''單詞類,比較定義是基于單詞長(zhǎng)度的'''def__new__(cls,word):#注意,我們使用了__new__,這是因?yàn)閟tr是一個(gè)不可變類型,#所以我們必須更早地初始化它(在創(chuàng)建時(shí))if''inword:print"單詞內(nèi)含有空格,截?cái)嗟降谝徊糠?word=word[:word.index('')]#在出現(xiàn)第一個(gè)空格之前全是字符了現(xiàn)在returnstr.__new__(cls,word)def__gt__(self,other):returnlen(self)>len(other)def__lt__(self,other):returnlen(self)=len(other)def__le__(self,other):returnlen(self)<=?len(other)
神奇方法數(shù)字:
就像你可以通過(guò)重載比較操作符的途徑來(lái)創(chuàng)建你自己的類實(shí)例,你同樣可以重載數(shù)字操作符。
一元操作符:
一元運(yùn)算和函數(shù)僅有一個(gè)操作數(shù),比如負(fù)數(shù),絕對(duì)值等。
__pos__(self)
實(shí)現(xiàn)一元正數(shù)的行為(如:+some_object)
__neg__(self)
實(shí)現(xiàn)負(fù)數(shù)的行為(如: -some_object)
__abs__(self)
實(shí)現(xiàn)內(nèi)建abs()函數(shù)的行為
__invert__(self)
實(shí)現(xiàn)用~操作符進(jìn)行的取反行為。
常規(guī)算數(shù)操作符:
現(xiàn)在我們涵蓋了基本的二元運(yùn)算符:+,-,*等等。其中大部分都是不言自明的。
__add__(self,other)
實(shí)現(xiàn)加法
__sub__(self,other)
實(shí)現(xiàn)減法
__mul__(self,other)
實(shí)現(xiàn)乘法
__floordiv__(self,other)
實(shí)現(xiàn)地板除法,使用//操作符
__div__(self,other)
實(shí)現(xiàn)傳統(tǒng)除法,使用/操作符
__truediv__(self,other)
實(shí)現(xiàn)真正除法。注意,只有當(dāng)你from __future__ import division時(shí)才會(huì)有效
__mod__(self,other)
實(shí)現(xiàn)求模,使用%操作符
__divmod__(self,other)
實(shí)現(xiàn)內(nèi)建函數(shù)divmod()的行為
__pow__(self,other)
實(shí)現(xiàn)乘方,使用**操作符
__lshift__(self,other)
實(shí)現(xiàn)左按位位移,使用<<操作符
__rshift__(self,other)
實(shí)現(xiàn)右按位位移,使用>>操作符
__and__(self,other)
實(shí)現(xiàn)按位與,使用&操作符
__or__(self,other)
實(shí)現(xiàn)按位或,使用|操作符
__xor__(self,other)
實(shí)現(xiàn)按位異或,使用^操作符
反射算數(shù)操作符:
首先舉個(gè)例子:some_object + other。這是“常規(guī)的”加法。而反射其實(shí)相當(dāng)于一回事,除了操作數(shù)改變了改變下位置:other + some_object。在大多數(shù)情況下,反射算術(shù)操作的結(jié)果等價(jià)于常規(guī)算術(shù)操作,所以你盡可以在剛重載完__radd__就調(diào)用__add__。干脆痛快:
__radd__(self,other)
實(shí)現(xiàn)反射加法
__rsub__(self,other)
實(shí)現(xiàn)反射減法
__rmul__(self,other)
實(shí)現(xiàn)反射乘法
__rfloordiv__(self,other)
實(shí)現(xiàn)反射地板除,用//操作符
__rdiv__(self,other)
實(shí)現(xiàn)傳統(tǒng)除法,用/操作符
__rturediv__(self,other)
實(shí)現(xiàn)真實(shí)除法,注意,只有當(dāng)你from __future__ import division時(shí)才會(huì)有效
__rmod__(self,other)
實(shí)現(xiàn)反射求模,用%操作符
__rdivmod__(self,other)
實(shí)現(xiàn)內(nèi)置函數(shù)divmod()的長(zhǎng)除行為,當(dāng)調(diào)用divmod(other,self)時(shí)被調(diào)用
__rpow__(self,other)
實(shí)現(xiàn)反射乘方,用**操作符
__rlshift__(self,other)
實(shí)現(xiàn)反射的左按位位移,使用<<操作符
__rrshift__(self,other)
實(shí)現(xiàn)反射的右按位位移,使用>>操作符
__rand__(self,other)
實(shí)現(xiàn)反射的按位與,使用&操作符
__ror__(self,other)
實(shí)現(xiàn)反射的按位或,使用|操作符
__rxor__(self,other)
實(shí)現(xiàn)反射的按位異或,使用^操作符
增量賦值:
Python也有各種各樣的神奇方法允許用戶自定義增量賦值行為。
這些方法都不會(huì)有返回值,因?yàn)橘x值在Python中不會(huì)有任何返回值。反而它們只是改變類的狀態(tài)。列表如下:
__rxor__(self,other)
實(shí)現(xiàn)加法和賦值
__isub__(self,other)
實(shí)現(xiàn)減法和賦值
__imul__(self,other)
實(shí)現(xiàn)乘法和賦值
__ifloordiv__(self,other)
實(shí)現(xiàn)地板除和賦值,用//=操作符
__idiv__(self,other)
實(shí)現(xiàn)傳統(tǒng)除法和賦值,用/=操作符
__iturediv__(self,other)
實(shí)現(xiàn)真實(shí)除法和賦值,注意,只有當(dāng)你from __future__ import division時(shí)才會(huì)有效
__imod__(self,other)
實(shí)現(xiàn)求模和賦值,用%=操作符
__ipow__(self,other)
實(shí)現(xiàn)乘方和賦值,用**=操作符
__ilshift__(self,other)
實(shí)現(xiàn)左按位位移和賦值,使用<<=操作符
__irshift__(self,other)
實(shí)現(xiàn)右按位位移和賦值,使用>>=操作符
__iand__(self,other)
實(shí)現(xiàn)按位與和賦值,使用&=操作符
__ior__(self,other)
實(shí)現(xiàn)按位或和賦值,使用|=操作符
__ixor__(self,other)
實(shí)現(xiàn)按位異或和賦值,使用^=操作符
類型轉(zhuǎn)換的神奇方法:
Python也有一組神奇方法被設(shè)計(jì)用來(lái)實(shí)現(xiàn)內(nèi)置類型轉(zhuǎn)換函數(shù)的行為,如float()。
__int__(self)
實(shí)現(xiàn)到int的類型轉(zhuǎn)換
__long__(self)
實(shí)現(xiàn)到long的類型轉(zhuǎn)換
__float__(self)
實(shí)現(xiàn)到float的類型轉(zhuǎn)換
__complex__(self)
實(shí)現(xiàn)到復(fù)數(shù)的類型轉(zhuǎn)換
__oct__(self)
實(shí)現(xiàn)到8進(jìn)制的類型轉(zhuǎn)換
__hex__(self)
實(shí)現(xiàn)到16進(jìn)制的類型轉(zhuǎn)換
__index__(self)
實(shí)現(xiàn)一個(gè)當(dāng)對(duì)象被切片到int的類型轉(zhuǎn)換。如果你自定義了一個(gè)數(shù)值類型,考慮到它可能被切片,所以你應(yīng)該重載__index__。
__trunc__(self)
當(dāng)math.trunc(self)被調(diào)用時(shí)調(diào)用。__trunc__應(yīng)當(dāng)返回一個(gè)整型的截?cái)啵?通常是long)。
__coerce__(self,other)
該方法用來(lái)實(shí)現(xiàn)混合模式的算術(shù)。如果類型轉(zhuǎn)換不可能那__coerce__應(yīng)當(dāng)返回None。否則,它應(yīng)當(dāng)返回一對(duì)包含self和other(2元組),且調(diào)整到具有相同的類型。
描述自定義類
用一個(gè)字符串來(lái)說(shuō)明一個(gè)類這通常是有用的。在Python中提供了一些方法讓你可以在你自己的類中自定義內(nèi)建函數(shù)返回你的類行為的描述。
__str__(self)
當(dāng)你定義的類中一個(gè)實(shí)例調(diào)用了str(),用于給它定義行為
__repr__(self)
當(dāng)你定義的類中一個(gè)實(shí)例調(diào)用了repr(),用于給它定義行為。str()和repr()主要的區(qū)別在于它的閱讀對(duì)象。repr()產(chǎn)生的輸出主要為計(jì)算機(jī)可讀(在很多情況下,這甚至可能是一些有效的Python代碼),而str()則是為了讓人類可讀。
__unicode__(self)
當(dāng)你定義的類中一個(gè)實(shí)例調(diào)用了unicode(),用于給它定義行為。unicode()像是str(),只不過(guò)它返回一個(gè)unicode字符串。警惕!如果用戶用你的類中的一個(gè)實(shí)例調(diào)用了str(),而你僅定義了__unicode__(),那它是不會(huì)工作的。以防萬(wàn)一,你應(yīng)當(dāng)總是定義好__str__(),哪怕用戶不會(huì)使用unicode。
__hash__(self)
當(dāng)你定義的類中一個(gè)實(shí)例調(diào)用了hash(),用于給它定義行為。它必須返回一個(gè)整型,而且它的結(jié)果是用于來(lái)在字典中作為快速鍵比對(duì)。
__nonzero__(self)
當(dāng)你定義的類中一個(gè)實(shí)例調(diào)用了bool(),用于給它定義行為。返回True或False,取決于你是否考慮一個(gè)實(shí)例是True或False的。
我們已經(jīng)相當(dāng)漂亮地干完了神奇方法無(wú)聊的部分(無(wú)示例),至此我們已經(jīng)討論了一些基礎(chǔ)的神奇方法,是時(shí)候讓我們向高級(jí)話題移動(dòng)了。
控制屬性訪問(wèn)
Python通過(guò)神奇的方法實(shí)現(xiàn)了大量的封裝,而不是通過(guò)明確的方法或字段修飾符。例如:
__getattr__(self,name)
你可以為用戶在試圖訪問(wèn)不存在(不論是存在或尚未建立)的類屬性時(shí)定義其行為。這對(duì)捕捉和重定向常見(jiàn)的拼寫(xiě)錯(cuò)誤,給出使用屬性警告是有用的(只要你愿意,你仍舊可選計(jì)算,返回那個(gè)屬性)或拋出一個(gè)AttributeError異常。這個(gè)方法只適用于訪問(wèn)一個(gè)不存在的屬性,所以,這不算一個(gè)真正封裝的解決之道。
__setattr__(self,name,value)
不像__getattr__,__setattr__是一個(gè)封裝的解決方案。它允許你為一個(gè)屬性賦值時(shí)候的行為,不論這個(gè)屬性是否存在。這意味著你可以給屬性值的任意變化自定義規(guī)則。然而,你需要在意的是你要小心使用__setattr__,在稍后的列表中會(huì)作為例子給出。
__delattr__
這等價(jià)于__setattr__,但是作為刪除類屬性而不是set它們。它需要相同的預(yù)防措施,就像__setattr__,防止無(wú)限遞歸(當(dāng)在__delattr__中調(diào)用del self.name會(huì)引起無(wú)限遞歸)。
__getattribute__(self,name)
__getattribute__良好地適合它的同伴們__setattr__和__delattr__。可我卻不建議你使用它。
__getattribute__只能在新式類中使用(在Python的最新版本中,所有的類都是新式類,在稍舊的版本中你可以通過(guò)繼承object類來(lái)創(chuàng)建一個(gè)新式類。它允許你定規(guī)則,在任何時(shí)候不管一個(gè)類屬性的值那時(shí)候是否可訪問(wèn)的。)它會(huì)因?yàn)樗耐橹械某鲥e(cuò)連坐受到某些無(wú)限遞歸問(wèn)題的困擾(這時(shí)你可以通過(guò)調(diào)用基類的__getattribute__方法來(lái)防止發(fā)生)。當(dāng)__getattribute__被實(shí)現(xiàn)而又只調(diào)用了該方法如果__getattribute__被顯式調(diào)用或拋出一個(gè)AttributeError異常,同時(shí)也主要避免了對(duì)__getattr__的依賴。這個(gè)方法可以使用,不過(guò)我不推薦它是因?yàn)樗幸粋€(gè)小小的用例(雖說(shuō)比較少見(jiàn),但我們需要特殊行為以獲取一個(gè)值而不是賦值)以及它真的很難做到實(shí)現(xiàn)0bug。
你可以很容易地在你自定義任何類屬性訪問(wèn)方法時(shí)引發(fā)一個(gè)問(wèn)題。參考這個(gè)例子:
def__setattr__(self,name,value):self.name=value#當(dāng)每次給一個(gè)類屬性賦值時(shí),會(huì)調(diào)用__setattr__(),這就形成了遞歸#因?yàn)樗嬲暮x是self.__setattr__('name',value)#所以這方法不停地調(diào)用它自己,變成了一個(gè)無(wú)法退出的遞歸最終引發(fā)crashdef__setattr__(self,name,value):self.__dict__[name]=value#給字典中的name賦值#在此自定義行為
以下是一個(gè)關(guān)于特殊屬性訪問(wèn)方法的實(shí)際例子(注意,我們使用super因?yàn)椴⒎撬蓄惗加衉_dict__類屬性):
classAccessCounter:'''一個(gè)類包含一個(gè)值和實(shí)現(xiàn)了一個(gè)訪問(wèn)計(jì)數(shù)器。當(dāng)值每次發(fā)生變化時(shí),計(jì)數(shù)器+1'''def__init__(self,val):super(AccessCounter,self).__setattr__('counter',0)super(AccessCounter,self).__setattr__('value',val)def__setattr__(self,name,value):ifname=='value':super(AccessCounter,self).__setattr__('counter',self.counter+1)#Makethisunconditional.#如果你想阻止其他屬性被創(chuàng)建,拋出AttributeError(name)異常super(AccessCounter,self).__setattr__(name,value)def__delattr__(self,name)ifname=='value':super(AccessCounter,self).__setattr__('counter',self.counter+1)super(AccessCounter,self).__delattr__(name)
制作自定義序列
很有多種方式可以讓你的類表現(xiàn)得像內(nèi)建序列(字典,元組,列表,字符串等)。這些是我迄今為止最喜歡的神奇方法了,因?yàn)椴缓侠淼目刂扑鼈冑x予了你一種魔術(shù)般地讓你的類實(shí)例整個(gè)全局函數(shù)數(shù)組漂亮工作的方式。
__len__(self)
返回容器的長(zhǎng)度。部分protocol同時(shí)支持可變和不可變?nèi)萜?/p>
__getitem__(self,key)
定義當(dāng)某一個(gè)item被訪問(wèn)時(shí)的行為,使用self[key]表示法。這個(gè)同樣也是部分可變和不可變?nèi)萜鱬rotocol。這也可拋出適當(dāng)?shù)漠惓?TypeError 當(dāng)key的類型錯(cuò)誤,或沒(méi)有值對(duì)應(yīng)Key時(shí)。
__setitem__(self,key,value)
定義當(dāng)某一個(gè)item被賦值時(shí)候的行為,使用self[key]=value表示法。這也是部分可變和不可變?nèi)萜鱬rotocol。再一次重申,你應(yīng)當(dāng)在適當(dāng)之處拋出KeyError和TypeError異常。
__delitem__(self,key)
定義當(dāng)某一個(gè)item被刪除(例如 del self[key])時(shí)的行為。這僅是部分可變?nèi)萜鞯膒rotocol。在一個(gè)無(wú)效key被使用后,你必須拋出一個(gè)合適的異常。
__iter__(self)
應(yīng)該給容器返回一個(gè)迭代器。迭代器會(huì)返回若干內(nèi)容,大多使用內(nèi)建函數(shù)iter()表示。當(dāng)一個(gè)容器使用形如for x in container:的循環(huán)。迭代器本身就是其對(duì)象,同時(shí)也要定義好一個(gè)__iter__方法來(lái)返回自身。
__reversed__(self)
當(dāng)定義調(diào)用內(nèi)建函數(shù)reversed()時(shí)的行為。應(yīng)該返回一個(gè)反向版本的列表。
__contains__(self,item)
__contains__為成員關(guān)系,用in和not in測(cè)試時(shí)定義行為。那你會(huì)問(wèn)這個(gè)為何不是一個(gè)序列的protocol的一部分?這是因?yàn)楫?dāng)__contains__未定義,Python就會(huì)遍歷序列,如果遇到正在尋找的item就會(huì)返回True。
__concat__(self,other)
最后,你可通過(guò)__concat__定義你的序列和另外一個(gè)序列的連接。應(yīng)該從self和other返回一個(gè)新構(gòu)建的序列。當(dāng)調(diào)用2個(gè)序列時(shí)__concat__涉及操作符+
在我們的例子中,讓我們看一下一個(gè)list實(shí)現(xiàn)的某些基礎(chǔ)功能性的構(gòu)建。可能會(huì)讓你想起你使用的其他語(yǔ)言(比如Haskell)。
classFunctionalList:'''類覆蓋了一個(gè)list的某些額外的功能性魔法,像head,tail,init,last,drop,andtake'''def__init__(self,values=None):ifvaluesisNone:self.values=[]else:self.values=valuesdef__len__(self):returnlen(self.values)def__getitem__(self,key):#如果key是非法的類型和值,那么listvaluse會(huì)拋出異常returnself.values[key]def__setitem__(self,key,value):self.values[key]=valuedef__delitem__(self,key):delself.values[key]def__iter__(self):returniter(self.values)def__reversed__(self):returnreversed(self.values)defappend(self,value):self.values.append(value)defhead(self):#獲得第一個(gè)元素returnself.values[0]deftail(self):#獲得在第一個(gè)元素后的其他所有元素returnself.values[1:]definit(self):#獲得除最后一個(gè)元素的序列returnself.values[:-1]deflast(last):#獲得最后一個(gè)元素returnself.values[-1]defdrop(self,n):#獲得除前n個(gè)元素的序列returnself.values[n:]deftake(self,n):#獲得前n個(gè)元素returnself.values[:n]
反射
你也可以通過(guò)定義神奇方法來(lái)控制如何反射使用內(nèi)建函數(shù)isinstance()和issubclass()的行為。這些神奇方法是:
__instancecheck__(self,instance)
檢查一個(gè)實(shí)例是否是你定義類中的一個(gè)實(shí)例(比如,isinstance(instance, class))
__subclasscheck__(self,subclass)
檢查一個(gè)類是否是你定義類的子類(比如,issubclass(subclass, class))
可調(diào)用對(duì)象
這是Python中一個(gè)特別的神奇方法,它允許你的類實(shí)例像函數(shù)。所以你可以“調(diào)用”它們,把他們當(dāng)做參數(shù)傳遞給函數(shù)等等。這是另一個(gè)強(qiáng)大又便利的特性讓Python的編程變得更可愛(ài)了。
__call__(self,[args…])
允許類實(shí)例像函數(shù)一樣被調(diào)用。本質(zhì)上,這意味著x()等價(jià)于x.__call__()。注意,__call__需要的參數(shù)數(shù)目是可變的,也就是說(shuō)可以對(duì)任何函數(shù)按你的喜好定義參數(shù)的數(shù)目定義__call__。
__call__可能對(duì)于那些經(jīng)常改變狀態(tài)的實(shí)例來(lái)說(shuō)是極其有用的。“調(diào)用”實(shí)例是一種順應(yīng)直覺(jué)且優(yōu)雅的方式來(lái)改變對(duì)象的狀態(tài)。下面一個(gè)例子是一個(gè)類表示一個(gè)實(shí)體在一個(gè)平面上的位置:
classEntity:'''描述實(shí)體的類,被調(diào)用的時(shí)候更新實(shí)體的位置'''def__init__(self,size,x,y):self.x,self.y=x,yself.size=sizedef__call__(self,x,y):'''改變實(shí)體的位置'''self.x,self.y=x,y#省略...
上下文管理器
上下文管理允許對(duì)對(duì)象進(jìn)行設(shè)置和清理動(dòng)作,用with聲明進(jìn)行已經(jīng)封裝的操作。上下文操作的行為取決于2個(gè)神奇方法:
__enter__(self)
定義塊用with聲明創(chuàng)建出來(lái)時(shí)上下文管理應(yīng)該在塊開(kāi)始做什么。
__exit__(self,exception_type,exception_value,traceback)
定義在塊執(zhí)行(或終止)之后上下文管理應(yīng)該做什么。
你也可以使用這些方法去創(chuàng)建封裝其他對(duì)象通用的上下文管理。看下面的例子:
classCloser:'''用with聲明一個(gè)上下文管理用一個(gè)close方法自動(dòng)關(guān)閉一個(gè)對(duì)象'''def__init__(self,obj):self.obj=objdef__enter__(self):returnself.obj#綁定目標(biāo)def__exit__(self,exception_type,exception_val,trace):try:self.obj.close()exceptAttributeError:#obj不具備closeprint'Notclosable.'returnTrue#成功處理異常
以下是一個(gè)對(duì)于Closer實(shí)際應(yīng)用的一個(gè)例子,使用一個(gè)FTP連接進(jìn)行的演示(一個(gè)可關(guān)閉的套接字):
>>>frommagicmethodsimportCloser>>>fromftplibimport:;;>>>withCloser(FTP('ftp.somsite.com'))asconn:...conn.dir()...#省略的輸出>>>conn.dir()#一個(gè)很長(zhǎng)的AttributeError消息,不能關(guān)閉使用的一個(gè)連接>>>withCloser(int(5))asi:...i+=1...Notcloseable.>>>i6
構(gòu)建描述符對(duì)象
描述符可以改變其他對(duì)象,也可以是訪問(wèn)類中任一的getting,setting,deleting。
作為一個(gè)描述符,一個(gè)類必須至少實(shí)現(xiàn)__get__,__set__,和__delete__中的一個(gè)。讓我們快點(diǎn)看一下這些神奇方法吧:
__get__(self,instance,owner)
當(dāng)描述符的值被取回時(shí)定義其行為。instance是owner對(duì)象的一個(gè)實(shí)例,owner是所有類。
__set__(self,instance,value)
當(dāng)描述符的值被改變時(shí)定義其行為。instance是owner對(duì)象的一個(gè)實(shí)例,value是設(shè)置的描述符的值
__delete__(self,instance)
當(dāng)描述符的值被刪除時(shí)定義其行為。instance是owner對(duì)象的一個(gè)實(shí)例。
現(xiàn)在,有一個(gè)有用的描述符應(yīng)用例子:?jiǎn)挝晦D(zhuǎn)換策略
classMeter(object):'''米描述符'''def__init__(self,value=0.0):self.value=float(value)def__get__(self,instance,owner):returnself.valuedef__set__(self,instance,value):self.value=float(value)classFoot(object):'''英尺描述符'''def__get__(self,instance,owner):returninstance.meter*3.2808def__set__(self,instance,value):instance.meter=float(value)/3.2808classDistance(object):'''表示距離的類,控制2個(gè)描述符:feet和meters'''meter=Meter()foot=Foot()
總結(jié)
這份指南的目標(biāo)就是讓任何人都能讀懂它,不管讀者們是否具備Python或面向?qū)ο蟮木幊探?jīng)驗(yàn)。如果你正準(zhǔn)備學(xué)習(xí)Python,那你已經(jīng)獲得了編寫(xiě)功能豐富、優(yōu)雅、易用的類的寶貴知識(shí)。如果你是一名中級(jí)Python程序員,你有可能已經(jīng)拾起了一些概念、策略和一些好的方法來(lái)減少你編寫(xiě)的代碼量。如果你是一名Python專家,你可能已經(jīng)回顧了某些你可能已經(jīng)遺忘的知識(shí)點(diǎn),或者你又又有一些新的發(fā)現(xiàn)。不管你的經(jīng)驗(yàn)等級(jí)如何,希望你在這次Python神奇方法之旅中有所收獲!
-
初始化
+關(guān)注
關(guān)注
0文章
49瀏覽量
11837 -
python
+關(guān)注
關(guān)注
56文章
4782瀏覽量
84464
原文標(biāo)題:【Python大神秘籍Top10】這些竅門(mén)99%的人都不知道
文章出處:【微信號(hào):AI_era,微信公眾號(hào):新智元】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論