目錄
一、說明
二、應用
(1)以下**只用到了__eq__,因為沒涉及一中的說明,這裡根本沒涉及hash
(2)如果自定義類重寫了__eq__()方法沒有重寫__hash__()方法,那麼這個類無法作為雜湊集合的元素使用(這個hashable collections指的是set、frozenset和dict)。
(3)如果自定義類重寫了__hash__()方法沒有重寫__eq__()方法
(4)hash與eq都重寫
注意魔法方法__hash__的使用場景有二:
(1)被內建函式hash()呼叫
(2)hash型別的集合對自身成員的hash操作:set(), frozenset([iterable]), dict(**kwarg)
以下是文件說明:
**版本3.6.3 文件版本:3.6.6
object.__hash__(self)
called by built-in function hash() and for operations on members of
hashed collections including set, frozenset, and dict.
(即使你把我注釋掉的__hash__放開也不會有任何對__hash__的呼叫)
class person:
def __init__(self, name, age):
self.name = name
self.age = age
# def __hash__(self):
# print(self.name, '使用了__hash__方法')
# return hash(self.name)
def __eq__(self, other):
print(self.name, '使用了__eq__方法')
比如雜湊集合放的全是物件,只定義的__eq__,沒定義__hash__,會報錯:
class person:
def __init__(self, name, age):
self.name = name
self.age = age
# def __hash__(self):
# print(self.name, '使用了__hash__方法')
# return hash(self.name)
def __eq__(self, other):
print(self.name, '使用了__eq__方法')
return self.__dict__ == other.__dict__
person1 = person('zs', 20)
person2 = person('ls', 20)
person3 = person('ww', 30)
person4 = person('zs', 20)
set1 =
print(set1)
結果:
這其實是因為重寫__eq__()方法後會預設把__hash__賦為none(文件後面有說),像list一樣。如下面測試:
class a:
def __eq__(self, other):
pass
a = a()
print(a.__hash__) # none
hash(a)
# typeerror: unhashable type: 'a'
class person:
def __init__(self, name, age):
self.name = name
self.age = age
def __hash__(self):
print(self.name, '使用了__hash__方法')
return hash(self.name)
# def __eq__(self, other):
# print(self.name, '使用了__eq__方法')
# return self.__dict__ == other.__dict__
person1 = person('zs', 20)
person2 = person('ls', 20)
person3 = person('ww', 30)
person4 = person('zs', 20)
set1 =
print(set1)
不報錯,但不符合需求———我們認為person1與person4是同乙個人,他們卻都被新增到集合,違背了集合的理念:
zs 使用了__hash__方法
ls 使用了__hash__方法
ww 使用了__hash__方法
zs 使用了__hash__方法
我們期望中沒有person4.為什麼這裡會有person4呢?而且person1與person4的hash(name)是相同的,為什麼最後還是加到了集合呢? 主要是因為當發現hash出的值相同時,就需要__eq__進行下一步判斷。我們重寫了__hash__卻沒重寫__eq__,預設呼叫了object的__eq__,而預設的__eq__比較的是物件位址,person1與persperson4位址不同,所以將最後的person4加到了集合.
可以看到person1與person4的hash相同,以為我們重寫的__hash__是根據名字,所以需要__eq__進一步判斷:
下面**是正確的:
class person:
def __init__(self, name, age):
self.name = name
self.age = age
def __hash__(self):
print(self.name, '使用了__hash__方法')
return hash(self.name)
def __eq__(self, other):
print(self.name, '使用了__eq__方法')
return self.__dict__.get("name") == other.__dict__.get("name")
person1 = person('zs', 20)
person2 = person('ls', 20)
person3 = person('ww', 30)
person4 = person('zs', 20)
set1 =
print(set1)
上面**執行原理:因為要加入集合,所以每個person都會呼叫__hash__,我們這裡重寫了,所以用我們重寫的__hash__。
我們重寫的__hash__是比較每個物件內容中的name屬性。當hash(self.name)值不同時,name肯定不同,所以就不會再呼叫
__eq__,直接將物件加入集合就好了;而當hash(name)值相同時,我們不能盲目認為不能加入集合,因為不同的name可能hash出一樣的值,(比如:若hash演算法是a%7 = b ,其中a 是要hash的引數,b是hash(a)後得到的位址值。當a = 7時 b=0,而當a =14時 , b 還是0,但是7和14明顯不同。),這時候就會再呼叫我們重寫的__eq__幫忙來判斷所有內容是否相同了。
何時使用或何時不使用malloc函式
在初學資料結構時,我們往往不太清楚在定義乙個結構體指標時要不要使用malloc函式。例如以下的 linklist init linklist retrun h linklist s s data x 以上這兩句 是不行的,因為s沒有指向確切的位址,所以不能通過s來向它要指向的位址賦值 但是linkl...
何時使用或何時不使用malloc函式
在初學資料結構時,我們往往不太清楚在定義乙個結構體指標時要不要使用malloc函式。例如以下的 linklist init linklist retrun h linklist s s data x 以上這兩句 是不行的,因為s沒有指向確切的位址,所以不能通過s來向它要指向的位址賦值 但是linkl...
何時選用sql或hadoop
很多朋友問時下如火如荼的 hadoop 是否適合引進我們自己的專案,什麼時候用 sql,什麼時候用 hadoop,它們之間如何取捨?aaron cordova 用一張圖來回答你這個問題,對於不同的資料場景,如何選取正確的資料儲存處理工具進行了詳細描述。aaron cordova 是美國大資料分析及架...