談?wù)刜_eq__和__hash__

今天刷一道算法題的時(shí)候用到了list_a == list_b的判斷,==is大家都已經(jīng)是耳熟能詳了,前者是判斷值是否相等,后者是判斷引用是否相等,在用==操作符進(jìn)行判斷的時(shí)候,其實(shí)內(nèi)部調(diào)用的是__eq__方法。比如

class Item:
  def __init__(self, val):
      self.val = val
  def __eq__(self, other):
      return self.val == other.val

first = Item('hello')
second = Item('hello)
print(first == second) # True

如果不實(shí)現(xiàn)__eq__方法,那么自定義類型會(huì)調(diào)用默認(rèn)的__eq__方法, 通過默認(rèn)方法進(jìn)行比較的相等條件相當(dāng)嚴(yán)格,只有自己和自己比才會(huì)返回True,表現(xiàn)如下

class Item:
  def __init__(self, val):
      self.val = val

first = Item('hello')
second = Item('hello)
print(first == second) # False

因此,在需要進(jìn)行自定義類型比較的時(shí)候,建議實(shí)現(xiàn)__eq__方法。

談及__eq__方法,就不得不談__hash__,兩者總是一起出現(xiàn).在Python中,如果自定義類定義了__eq__而未定義__hash__方法的話,那么默認(rèn)將__hash__方法設(shè)置為None。這會(huì)有什么潛在問題呢?

Python中的對(duì)象分為可變和不可變對(duì)象,我們從另一個(gè)角度來看,可以分為可哈希對(duì)象和不可哈希對(duì)象。通俗的說,可哈希對(duì)象可以作為字典的鍵,不可哈希對(duì)象無法作為字典的鍵。有時(shí)候,我們使用列表或者自定義對(duì)象作為字典的鍵,或者使用set進(jìn)行元素去重的時(shí)候,會(huì)遇到unhashable type: xxx之類的問題,這類問題出現(xiàn)的原因就是字典的鍵或者集合中的元素類型為不可哈希類型。

那么常見的不可哈希類型有哪些呢?幾乎都是一些常見的可變類型,比如列表、集合和字典等,都是不可哈希類型。有時(shí)候我們有對(duì)元素類型為不可變類型的對(duì)象進(jìn)行去重或者使用它作為字典的key的需求,這個(gè)時(shí)候又該怎么做呢?

我們可以自定義一個(gè)類,來實(shí)現(xiàn)__eq____hash__方法達(dá)到這個(gè)效果,且看下面代碼

class It(list):
    def __init__(self, vals):
        self.vals = vals
    def __eq__(self, other):
        return self.vals == other.vals
    def __hash__(self):
        # 注意__hash__需要返回一個(gè)整數(shù)
        return hash(';'.join(vals))

s = set()
j = It(['a', 'b'])
s.add(j)
print(j in set) # True
k = It(['a', 'b'])
print(k in set) # True

從上面結(jié)果我們可以看到k這個(gè)實(shí)例并未加入s這個(gè)集合,但是在判斷時(shí)返回了True,原因就是jk兩者的__hash____eq__運(yùn)算結(jié)果相同

我們改改代碼,再看看結(jié)果

class It(list):
    def __init__(self, vals):
        self.vals = vals
    def __eq__(self, other):
        # 這里如果用 self == other就會(huì)出現(xiàn)無限遞歸,讀者可以思考為什么
        return id(self) == id(other)
    def __hash__(self):
        # 注意__hash__需要返回一個(gè)整數(shù)
        return hash(';'.join(vals))

s = set()
j = It(['a', 'b'])
s.add(j)
print(j in set) # True
k = It(['a', 'b'])
print(k in set) # False

可以看到在關(guān)于通過hash運(yùn)算判斷兩個(gè)對(duì)象是否映射成一個(gè)值是需要__hash____eq__方法共同決定的

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,582評(píng)論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,136評(píng)論 1 32
  • 〇、前言 本文共108張圖,流量黨請(qǐng)慎重! 歷時(shí)1個(gè)半月,我把自己學(xué)習(xí)Python基礎(chǔ)知識(shí)的框架詳細(xì)梳理了一遍。 ...
    Raxxie閱讀 19,029評(píng)論 17 410
  • 文/妖精婆婆 這是一個(gè)70后的故事,關(guān)于愛情,關(guān)于選擇;這是一個(gè)普通人的必經(jīng)之路,關(guān)于離別,關(guān)于孤獨(dú)。 01 19...
    妖精婆婆閱讀 2,263評(píng)論 48 82
  • 我也不知道 你會(huì)在 什么時(shí)間 什么地點(diǎn) 什么場合 向我求婚 我想 我會(huì)哭 這個(gè)時(shí)候 ...
    呆萌的懶懶貓閱讀 301評(píng)論 1 2