今天刷一道算法題的時(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中的對象分為可變和不可變對象蒋纬,我們從另一個(gè)角度來看,可以分為可哈希對象和不可哈希對象坚弱。通俗的說蜀备,可哈希對象可以作為字典的鍵,不可哈希對象無法作為字典的鍵荒叶。有時(shí)候碾阁,我們使用列表或者自定義對象作為字典的鍵,或者使用set
進(jìn)行元素去重的時(shí)候些楣,會(huì)遇到unhashable type: xxx
之類的問題脂凶,這類問題出現(xiàn)的原因就是字典的鍵或者集合中的元素類型為不可哈希類型宪睹。
那么常見的不可哈希類型有哪些呢?幾乎都是一些常見的可變類型艰猬,比如列表横堡、集合和字典等,都是不可哈希類型冠桃。有時(shí)候我們有對元素類型為不可變類型的對象進(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
樱报,原因就是j
和k
兩者的__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è)對象是否映射成一個(gè)值是需要__hash__
和__eq__
方法共同決定的