- 當對兩個點的實例進行值的比較時,比如
p1=Point(1,1) p2=Point(1,2)
,判斷p1==p2
時__eq__()
會被調用,用以判斷兩個實例是否相等。在上述代碼中定義了只要x和y的坐標相同,兩個點相等。需要注意,__eq__()
對is
不生效,==
是比較的值,而is
比較的是引用,也就是內存地址。舉個例子,p1=Point(1,1) p2=Point(1,1)
,p1==p2
為True
,p1 is p2
為False
,只有p1 is p1
為True
。 - 在Python中對象分為可哈希對象和不可哈希對象,可哈希對象如字符串、數字、自定義的類、frozenset、元組,被稱作不可變對象,不可哈希對象如字典、列表、集合,被稱作可變對象。這里的不可變不是對象的值不可變,而是指對象創建后其hash值在其生命周期內不會改變。用函數
hash()
取可哈希對象的hash值,只要是同一對象其hash值不會改變;而對不可哈希對象取hash值,例如對列表取hash值,會報錯,返回TypeError: unhashable type: 'list'
。可哈希對象因其hash值不變可以用作字典的key,而不可哈希對象則不行。
- 當需要對類的一個實例取其hash值時,會調用
__hash__()
。一般來說,會把實例的所有屬性打包成元組,返回其hash值,從而實現自定義__hash__()
。在用set()
去重時就是對比hash值是否一樣,如果兩個對象hash值一樣代表重復。 - 用戶定義的類默認帶有
__eq__()
和__hash__()
方法;使用它們與任何對象(自己除外)比較必定不相等,並且x.__hash__()
會返回一個恰當的值以確保x == y
同時意味着x is y
且hash(x) == hash(y)
。 - 如果一個類沒有定義
__eq__()
方法,那么也不應該定義__hash__()
操作;如果它定義了__eq__()
但沒有定義__hash__()
,那么__hash__()
會被隱式地設為None
,這個類就變成了不可哈希對象。如果一個類定義了可變對象並實現了__eq__()
方法,則不應該實現__hash__()
,因為可哈希集的實現要求鍵的哈希集是不可變的。例如,Point
類中添加一個屬性li
是一個列表,由於列表不可哈希所以強行放入包含屬性的元組中並返回其哈希值會報錯。 - 如果使用默認的
__hash__()
則不論如何改變一個實例的值其hash值都不變;反之,使用本文這種自定義的__hash__()
方法,實例的值改變后,hash值就會改變。因此,自定義__hash__()
方法的類的實例不應該作為字典的key(強行作為key不會報錯,但是改變實例的屬性值會導致找不到key對應的value),key的哈希值必須唯一不可變,key的hash值改變會導致找不到key對應的value。