python 何時單用 hash 或 eq

2021-09-09 07:39:42 字數 4239 閱讀 8656

目錄

一、說明

二、應用

(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 是美國大資料分析及架...