python魔法方法-自定義序列詳解
自定義序列的相關(guān)魔法方法允許我們自己創(chuàng)建的類擁有序列的特性,讓其使用起來就像 python 的內(nèi)置序列(dict,tuple,list,string等)。
如果要實現(xiàn)這個功能,就要遵循 python 的相關(guān)的協(xié)議。所謂的協(xié)議就是一些約定內(nèi)容。例如,如果要將一個類要實現(xiàn)迭代,就必須實現(xiàn)兩個魔法方法:__iter__、next(python3.x中為__new__)。__iter__應(yīng)該返回一個對象,這個對象必須實現(xiàn) next 方法,通常返回的是 self 本身。而 next 方法必須在每次調(diào)用的時候都返回下一個元素,并且當(dāng)元素用盡時觸發(fā) StopIteration 異常。
而其實 for 循環(huán)的本質(zhì)就是先調(diào)用對象的__iter__方法,再不斷重復(fù)調(diào)用__iter__方法返回的對象的 next 方法,觸發(fā) StopIteration 異常時停止,并內(nèi)部處理了這個異常,所以我們看不到異常的拋出。
這種關(guān)系就好像接口一樣,如果回顧以前幾篇的魔法方法,可以發(fā)現(xiàn)許多的內(nèi)置函數(shù)得到的結(jié)果就是相應(yīng)的魔法方法的返回值。
下面是一下相關(guān)的魔法方法:
•__len__(self)
•返回容器的長度??勺兒筒豢勺?nèi)萜鞫家獙崿F(xiàn)它,這是協(xié)議的一部分。
•__getitem__(self, key)
•定義當(dāng)某一項被訪問時,使用self[key]所產(chǎn)生的行為。這也是可變?nèi)萜骱筒豢勺內(nèi)萜鲄f(xié)議的一部分。如果鍵的類型錯誤將產(chǎn)生TypeError;如果key沒有合適的值則產(chǎn)生KeyError。
•__setitem__(self, key, value)
•定義當(dāng)一個條目被賦值時,使用self[key] = value所產(chǎn)生的行為。這也是可變?nèi)萜鲄f(xié)議的一部分。而且,在相應(yīng)的情形下也會產(chǎn)生KeyError和TypeError。
•__delitem__(self, key)
•定義當(dāng)某一項被刪除時所產(chǎn)生的行為。(例如del self[key])。這是可變?nèi)萜鲄f(xié)議的一部分。當(dāng)你使用一個無效的鍵時必須拋出適當(dāng)?shù)漠惓!?/p>
•__iter__(self)
•返回一個容器迭代器,很多情況下會返回迭代器,尤其是當(dāng)內(nèi)置的iter()方法被調(diào)用的時候,以及當(dāng)使用for x in container:方式循環(huán)的時候。迭代器是它們本身的對象,它們必須定義返回self的__iter__方法。
•__reversed__(self)
•實現(xiàn)當(dāng)reversed()被調(diào)用時的行為。應(yīng)該返回序列反轉(zhuǎn)后的版本。僅當(dāng)序列是有序的時候?qū)崿F(xiàn)它,例如列表或者元組。
•__contains__(self, item)
•定義了調(diào)用in和not in來測試成員是否存在的時候所產(chǎn)生的行為。這個不是協(xié)議要求的內(nèi)容,但是你可以根據(jù)自己的要求實現(xiàn)它。當(dāng)__contains__沒有被定義的時候,Python會迭代這個序列,并且當(dāng)找到需要的值時會返回True。
•__missing__(self, key)
•其在dict的子類中被使用。它定義了當(dāng)一個不存在字典中的鍵被訪問時所產(chǎn)生的行為。(例如,如果我有一個字典d,當(dāng)"george"不是字典中的key時,使用了d["george"],此時d.__missing__("george")將會被調(diào)用)。
下面是一個代碼示例:
class Foo(object): def __init__(self, key, value): self.key = [] self.value = [] self.key.append(key) self.value.append(value) def __len__(self): return len(self.key) def __getitem__(self, item): try: __index = self.key.index(item) return self.value[__index] except ValueError: raise KeyError('can not find the key') def __setitem__(self, key, value): if key not in self.key: self.key.append(key) self.value.append(value) else: __index = self.key.index(key) self.value[__index] = value def __delitem__(self, key): try: __index = self.key.index(key) del self.key[__index] del self.value[__index] except ValueError: raise KeyError('can not find the key') def __str__(self): result_list = [] for index in xrange(len(self.key)): __key = self.key[index] __value = self.value[index] result = __key, __value result_list.append(result) return str(result_list) def __iter__(self): self.__index = 0 return self def next(self): if self.__index == len(self.key): self.__index = 0 raise StopIteration() else: __key = self.key[self.__index] __value = self.value[self.__index] result = __key, __value self.__index += 1 return result def __reversed__(self): __result = self.value[:] __result.reverse() return __result def __contains__(self, item): if item in self.value: return True else: return False
這里創(chuàng)建一個模擬字典的類,這個類的內(nèi)部維護了兩個列表,key 負責(zé)儲存鍵,value 負責(zé)儲存值,兩個列表通過索引的一一對應(yīng),從而達到模擬字典的目的。
首先,我們看看__len__方法,按照協(xié)議,這個方法應(yīng)該返回容器的長度,因為這個類在設(shè)計的時候要求兩個列表必須等長,所以理論上返回哪個列表的長度都是一樣的,這里我選擇返回 key 的長度。
然后是__getitem__方法。這個方法會在a['scolia']時,調(diào)用a.__getitem__('scolia')。也就是說這個方法定義了元素的獲取,我這里的思路是先找到 key 列表中建的索引,然后用索引去 value 列表中找對應(yīng)的元素,然后將其返回。然后為了進一步偽裝成字典,我捕獲了可能產(chǎn)生的 ValueError (這是 item 不在 key 列表中時觸發(fā)的異常),并將其偽裝成字典找不到鍵時的 KeyError。
理論上只要實現(xiàn)了上面兩個方法,就可以得到一個不可變的容器了。但是我覺得并不滿意所以繼續(xù)拓展。
__setitem__(self, key, value)方法定義了 a['scolia'] = 'good' 這種操作時的行為,此時將會調(diào)用a.__setitem__('scolia', 'good') 因為是綁定方法,所以self是自動傳遞的,我們不用理。這里我也模擬了字典中對同一個鍵賦值時會造成覆蓋的特性。這個方法不用返回任何值,所以return語句也省略了。
__delitem__(self, key)方法定義了del a['scolia'] 這類操作時候的行為,里面的‘scolia'就作為參數(shù)傳進去。這里也進行了異常的轉(zhuǎn)換。
只有實現(xiàn)里以上四個方法,就可以當(dāng)做可變?nèi)萜鱽硎褂昧?。有同學(xué)可能發(fā)現(xiàn)并沒有切片對應(yīng)的魔法方法,而事實上,我也暫時沒有找到先,這部分內(nèi)容先擱著一邊。
接下來的 __str__ 是對應(yīng)于 str() 函數(shù),在類的表示中會繼續(xù)討論,這里是為了 print 語句好看才加進去的,因為print語句默認就是調(diào)用str()函數(shù)。
__iter__和next方法在開頭的時候討論過了,這里是為了能讓其進行迭代操作而加入的。
__reversed__(self)方法返回一個倒序后的副本,這里體現(xiàn)了有序性,當(dāng)然是否需要還是要看個人。
__contains__實現(xiàn)了成員判斷,這里我們更關(guān)心value列表中的數(shù)據(jù),所以判斷的是value列表。該方法要求返回布爾值。
下面是相應(yīng)的測試:
a = Foo('scolia', 'good') a[123] = 321 a[456] = 654 a[789] = 987 print a del a[789] print a for x, y in a: print x, y print reversed(a) print 123 in a print 321 in a
•__missing__(self, key)
class Boo(dict): def __new__(cls, *args, **kwargs): return super(Boo, cls).__new__(cls) def __missing__(self, key): return 'The key(%s) can not be find.'% key
測試:
b = Boo() b['scolia'] = 'good' print b['scolia'] print b['123']
當(dāng)然你也可以在找不到 key 的時候觸發(fā)異常,具體實現(xiàn)看個人需求。
以上這篇python魔法方法-自定義序列詳解就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python使用cn2an實現(xiàn)中文數(shù)字與阿拉伯?dāng)?shù)字的相互轉(zhuǎn)換
這篇文章主要介紹了Python使用cn2an實現(xiàn)中文數(shù)字與阿拉伯?dāng)?shù)字的相互轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03python list 查詢是否存在并且并返回下標(biāo)的操作
這篇文章主要介紹了python list 查詢是否存在并且并返回下標(biāo)的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-05-05PyQt5實現(xiàn)QLineEdit正則表達式輸入驗證器
這篇文章主要介紹了PyQt5實現(xiàn)QLineEdit正則表達式輸入驗證器,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04利用Python實現(xiàn)Shp格式向GeoJSON的轉(zhuǎn)換方法
JSON(JavaScript Object Nonation)是利用鍵值對+嵌套來表示數(shù)據(jù)的一種格式,以其輕量、易解析的優(yōu)點,這篇文章主要介紹了利用Python實現(xiàn)Shp格式向GeoJSON的轉(zhuǎn)換,需要的朋友可以參考下2019-07-07Python實現(xiàn)數(shù)據(jù)結(jié)構(gòu)線性鏈表(單鏈表)算法示例
這篇文章主要介紹了Python實現(xiàn)數(shù)據(jù)結(jié)構(gòu)線性鏈表(單鏈表)算法,結(jié)合實例形式分析了Python單鏈表的定義、節(jié)點插入、刪除、打印等相關(guān)操作技巧,需要的朋友可以參考下2019-05-05