Python判斷兩個(gè)對(duì)象相等的原理
概述
大部分的python程序員平時(shí)編程的時(shí)候,很少關(guān)心兩個(gè)對(duì)象為什么相等,因?yàn)榻坛毯徒?jīng)驗(yàn)來(lái)說(shuō),他們就應(yīng)該相等,比如1==1就應(yīng)該返回True,可是當(dāng)我們想要定義自己的對(duì)象或者修改默認(rèn)的對(duì)象行為時(shí),通常會(huì)因?yàn)椴涣私庠矶鴮?dǎo)致各種奇奇怪怪的錯(cuò)誤。
兩個(gè)對(duì)象如何相等
兩個(gè)對(duì)象如何才能相等要比我們想象的復(fù)雜很多,但核心的方法是重寫 eq 方法,這個(gè)方法返回True,則表示兩個(gè)對(duì)象相等,否則,就不相等。相反的,如果兩個(gè)對(duì)象不相等,則重寫 ne 方法。 默認(rèn)情況下,如果你沒(méi)有實(shí)現(xiàn)這個(gè)方法,則使用父類(object)的方法。父類的方法比較是的兩個(gè)對(duì)象的ID(可以通過(guò)id方法獲取對(duì)象ID),也就是說(shuō),如果對(duì)象的ID相等,則兩個(gè)對(duì)象也就相等。因此,我們可以得知,默認(rèn)情況下,對(duì)象只和自己相等。例如:
>>> class A(object): ... pass ... >>> >>> a = A() >>> b = A() >>> a == a True >>> a == b False >>> id(a) 4343310992 >>> id(b) 4343310928
Python2程序員經(jīng)常犯的一個(gè)錯(cuò)誤是,只重寫了 eq 方法,而沒(méi)有重寫 ne 方法,導(dǎo)致不可預(yù)計(jì)的錯(cuò)誤。而Python3會(huì)自動(dòng)重寫 ne 方法,如果你沒(méi)有重寫的話。
對(duì)象的Hash方法
Python里可Hash的對(duì)象,都有一個(gè)數(shù)字ID代表了它在python里的值,這個(gè)ID是由對(duì)象的 hash 方法返回的。因此,如果想讓一個(gè)對(duì)象可Hash,那必須實(shí)現(xiàn) hash 方法和之前提到的 eq 方法。和對(duì)象相等一樣,默認(rèn)情況下,對(duì)象的 hash 方法繼承自O(shè)bject對(duì)象,而Object對(duì)象的 hash 方法只計(jì)算對(duì)象ID,因此兩個(gè)對(duì)象始終擁有兩個(gè)不一樣的hash id,不管他們是多么相似。 當(dāng)我們把一個(gè)不可Hash的對(duì)象加入到set或者dict時(shí),會(huì)發(fā)生什么了?
>>> set().add({}) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'dict' unhashable type: 'dict'
原因是set()和dict()使用對(duì)象的hash值作為內(nèi)部索引,以便能快速索引到指定對(duì)象。因此,同一個(gè)對(duì)象返回相同的hash id就很重要了。
對(duì)象的Hash值在它的生命周期內(nèi)不能改變
如果你想定義一個(gè)比較完美的對(duì)象,并且實(shí)現(xiàn)了 eq 和 hash 方法來(lái)定義對(duì)象的比較行為和hash值,那么你就需要保證對(duì)象的相關(guān)屬性不能發(fā)生更改。不然會(huì)導(dǎo)致很詭異的錯(cuò)誤,比如下面的例子。
>>> class C: ... def __init__(self, x): ... self.x = x ... def __repr__(self): ... return "C({"+str(self.x)+"})" ... def __hash__(self): ... return hash(self.x) ... def __eq__(self, other): ... return ( ... self.__class__ == other.__class__ and ... self.x == other.x ... ) >>> d = dict() >>> s = set() >>> c = C(1) >>> d[c] = 42 >>> s.add(c) >>> d, s ({C(1): 42}, {C(1)}) >>> c in s and c in d # c is in both! True >>> c.x = 2 >>> c in s or c in d # c is in neither!? False >>> d, s ({C(2): 42}, {C(2)}) # but...it's right there!
在我們沒(méi)有修改對(duì)象的屬性時(shí)(c.x=2)之前,所有行為都符合預(yù)期。當(dāng)我們通過(guò)c.x=2時(shí)修改屬性后,執(zhí)行c in s or c in d返回False,但是內(nèi)容卻是修改后的,是不是很奇怪。這也就解釋了為什么str、tuple是可Hash的,而list和dict是不可hash的。
因此我們可以得出結(jié)論,如果兩個(gè)對(duì)象相等的話,那它們的hash值必然也是相等的。
總結(jié)
講了這么多有什么用了。 1. 當(dāng)我們遇到unhashable type這個(gè)異常時(shí),我們能夠知道為什么報(bào)這個(gè)錯(cuò)誤。 2. 如果定義了一個(gè)可比較的對(duì)象,那么最好保證對(duì)象hash值相關(guān)的屬性在生命周期內(nèi)不能發(fā)生改變,不然會(huì)發(fā)生意想不到的錯(cuò)誤。
以上所述是小編給大家介紹的Python判斷兩個(gè)對(duì)象相等的原理,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
200個(gè)Python?標(biāo)準(zhǔn)庫(kù)總結(jié)
這篇文章主要給大家分享了200個(gè)Python?標(biāo)準(zhǔn)庫(kù)總結(jié),主要對(duì)文本、數(shù)據(jù)類型、數(shù)學(xué)等多個(gè)類型總結(jié),既有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-01-01Python超簡(jiǎn)單容易上手的畫圖工具庫(kù)(適合新手)
這篇文章主要給大家介紹了關(guān)于Python超簡(jiǎn)單容易上手的畫圖工具庫(kù)的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05Python中的random.choices函數(shù)用法詳解
這篇文章主要給大家介紹了關(guān)于Python中random.choices函數(shù)用法的相關(guān)資料,random.random()?的功能是隨機(jī)返回一個(gè)?0-1范圍內(nèi)的浮點(diǎn)數(shù),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08Python尾遞歸優(yōu)化實(shí)現(xiàn)代碼及原理詳解
這篇文章主要介紹了Python尾遞歸優(yōu)化實(shí)現(xiàn)代碼及原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10python基于Node2Vec實(shí)現(xiàn)節(jié)點(diǎn)分類及其可視化示例詳解
這篇文章主要為大家介紹了python基于Node2Vec實(shí)現(xiàn)節(jié)點(diǎn)分類及其可視化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04Python基于pycrypto實(shí)現(xiàn)的AES加密和解密算法示例
這篇文章主要介紹了Python基于pycrypto實(shí)現(xiàn)的AES加密和解密算法,結(jié)合實(shí)例形式分析了Python使用pycrypto模塊進(jìn)行AES加密與解密操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-04-04