網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
Python使用__hash__和__eq__的問題
- 代碼版本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__()方法會(huì)被上述四種情況調(diào)用。
If a class does not define an __eq__() method it should not define a __hash__()operation either; if it defines __eq__() but not __hash__(), its instances will not be usable as items in hashable collections. If a class defines mutable objects and implements an __eq__() method, it should not implement __hash__(), since the implementation of hashable collections requires that a key’s hash value is immutable (if the object’s hash value changes, it will be in the wrong hash bucket).
如果自定義類沒定義__eq__()方法,那也不應(yīng)該定義__hash__()方法。?
如果定義了__eq__()方法沒有定義__hash__()方法,那么它無(wú)法作為哈希集合的元素使用(這個(gè)hashable collections值得是set、frozenset和dict)。
這其實(shí)是因?yàn)橹貙慱_eq__()方法后會(huì)默認(rèn)把__hash__賦為None(文檔后面有說(shuō)),像list一樣。?
class A:
? ? def __eq__(self, other):
? ? ? ? pass
?
?
a = A()
?
print(a.__hash__) ?# None
hash(a)
?
?
# TypeError: unhashable type: 'A'
還專門說(shuō)明:如果定義可變對(duì)象的類實(shí)現(xiàn)了__eq__()方法,就不要再實(shí)現(xiàn)__hash__()方法,否則這個(gè)對(duì)象的hash值發(fā)生變化會(huì)導(dǎo)致被放在錯(cuò)誤的哈希桶中。這個(gè)可以用字典試一下,你的鍵值不在是一一對(duì)應(yīng)的,只要能讓這兩個(gè)方法返回一致的對(duì)象都能改動(dòng)那個(gè)本不屬于自己的值,這篇文章的第五個(gè)例子就是這種情況。
User-defined classes have __eq__() and __hash__() methods by default; with them, all objects compare unequal (except with themselves) and x.__hash__() returns an appropriate value such that x == y implies both that x is y and hash(x) == hash(y).?
用戶定義的類默認(rèn)都有__eq__()和__hash__()方法,這是從object繼承的,如果你不重寫任何一個(gè),那么對(duì)這個(gè)類的兩個(gè)實(shí)例x,y來(lái)說(shuō),x is y ,x == y , hash(x) == hash(y)會(huì)同時(shí)成立/不成立,即只有在x就是y的時(shí)候成立。
A class that overrides __eq__() and does not define __hash__() will have its __hash__()implicitly set to None. When the __hash__() method of a class is None, instances of the class will raise an appropriate TypeError when a program attempts to retrieve their hash value, and will also be correctly identified as unhashable when checking isinstance(obj, collections.abc.Hashable).
?重寫了__eq__()方法的類會(huì)隱式的把__hash__賦為None。當(dāng)獲取實(shí)例的哈希值即用到了__hash__()方法時(shí)(只有上文提到的四種情況會(huì)用到這個(gè)方法)就會(huì)拋出TypeError錯(cuò)誤,上文例子演示過(guò)了。
并且isinstance判斷類型也能正確判斷。
# 直接安裝不成功 ?pip install collections2 才行
# collections2==0.3.0 ?A set of improved data types inspired by the standard library's collections module.
import collections
?
?
class A:
? ? def __eq__(self, other):
? ? ? ? pass
?
?
class B:
? ? pass
?
?
a = A()
b = B()
?
print(isinstance(a, collections.abc.Hashable)) ?# False
print(isinstance(b, collections.abc.Hashable)) ?# True
If a class that overrides __eq__() needs to retain the implementation of __hash__() from a parent class, the interpreter must be told this explicitly by setting __hash__ =<ParentClass>.__hash__.
If a class that does not override __eq__() wishes to suppress hash support, it should include __hash__ = None in the class definition. A class which defines its own __hash__() that explicitly raises a TypeError would be incorrectly identified as hashable by an isinstance(obj, collections.abc.Hashable) call.
?如果一個(gè)類重寫了__eq__()方法還需要能使用父類的__hash__()方法(上文已說(shuō)默認(rèn)情況下是被賦值為None了),那就需要明確的說(shuō)明一下:例class A;如果一個(gè)類沒有重寫__eq__()方法而又需要讓__hash__()失效,那就要明確的賦值為None,像list、set等的源碼那樣。
如果你重寫了一個(gè)會(huì)拋出異常的__hash__()方法,雖然使用時(shí)會(huì)拋出異常,但是類型判斷還是會(huì)判斷為是可哈希的,這是要注意的:例class B。
import collections
?
?
class A:
? ? def __eq__(self, other):
? ? ? ? pass
? ? __hash__ = object.__hash__
?
?
class B:
? ? def __hash__(self):
? ? ? ? raise TypeError('There is an error!')
?
?
a = A()
b = B()
?
print(isinstance(a, collections.abc.Hashable))
print(isinstance(b, collections.abc.Hashable))
hash(b)
?
?
# 結(jié)果:
# True
# True
# ...line 12, in __hash__...
# TypeError: There is an error!
文檔位置:3.6.6 object.__hash__
Python類中特殊方法__eq__和__hash__關(guān)系
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return repr((self.id, self.x, self.y))
def __eq__(self, other):
return self.x == other.y and self.y == self.y
def __hash__(self):
return hash((self.x, self.y))
上面定義了一個(gè)二維點(diǎn)的類其中__repr__主要用來(lái)以一個(gè)字符串表示該類的實(shí)例,例如Point(1,2),在調(diào)試時(shí)打印該點(diǎn)會(huì)獲得字符串(1,2)。
當(dāng)對(duì)兩個(gè)點(diǎn)的實(shí)例進(jìn)行值的比較時(shí),比如p1=Point(1,1) p2=Point(1,2),判斷p1==p2時(shí)__eq__()會(huì)被調(diào)用,用以判斷兩個(gè)實(shí)例是否相等。在上述代碼中定義了只要x和y的坐標(biāo)相同,兩個(gè)點(diǎn)相等。需要注意,__eq__()對(duì)is不生效,==是比較的值,而is比較的是引用,也就是內(nèi)存地址。舉個(gè)例子,p1=Point(1,1) p2=Point(1,1),p1==p2為True,p1 is p2為False,只有p1 is p1為True。
在Python中對(duì)象分為可哈希對(duì)象和不可哈希對(duì)象,可哈希對(duì)象如字符串、數(shù)字、自定義的類、frozenset、元組,被稱作不可變對(duì)象,不可哈希對(duì)象如字典、列表、集合,被稱作可變對(duì)象。這里的不可變不是對(duì)象的值不可變,而是指對(duì)象創(chuàng)建后其hash值在其生命周期內(nèi)不會(huì)改變。用函數(shù)hash()取可哈希對(duì)象的hash值,只要是同一對(duì)象其hash值不會(huì)改變;而對(duì)不可哈希對(duì)象取hash值,例如對(duì)列表取hash值,會(huì)報(bào)錯(cuò),返回TypeError: unhashable type: 'list'。可哈希對(duì)象因其hash值不變可以用作字典的key,而不可哈希對(duì)象則不行。
當(dāng)需要對(duì)類的一個(gè)實(shí)例取其hash值時(shí),會(huì)調(diào)用__hash__()。一般來(lái)說(shuō),會(huì)把實(shí)例的所有屬性打包成元組,返回其hash值,從而實(shí)現(xiàn)自定義__hash__()。在用set()去重時(shí)就是對(duì)比hash值是否一樣,如果兩個(gè)對(duì)象hash值一樣代表重復(fù)。
用戶定義的類默認(rèn)帶有__eq__()和 __hash__()方法;使用它們與任何對(duì)象(自己除外)比較必定不相等,并且 x.__hash__()會(huì)返回一個(gè)恰當(dāng)?shù)闹狄源_保 x == y 同時(shí)意味著 x is y且 hash(x) == hash(y)。
如果一個(gè)類沒有定義__eq__()方法,那么也不應(yīng)該定義 __hash__()操作;如果它定義了__eq__()但沒有定義 __hash__(),那么__hash__()會(huì)被隱式地設(shè)為None,這個(gè)類就變成了不可哈希對(duì)象。如果一個(gè)類定義了可變對(duì)象并實(shí)現(xiàn)了 __eq__()方法,則不應(yīng)該實(shí)現(xiàn)__hash__(),因?yàn)榭晒<膶?shí)現(xiàn)要求鍵的哈希集是不可變的。例如,Point類中添加一個(gè)屬性li是一個(gè)列表,由于列表不可哈希所以強(qiáng)行放入包含屬性的元組中并返回其哈希值會(huì)報(bào)錯(cuò)。
如果使用默認(rèn)的__hash__()則不論如何改變一個(gè)實(shí)例的值其hash值都不變;反之,使用本文這種自定義的__hash__()方法,實(shí)例的值改變后,hash值就會(huì)改變。因此,自定義__hash__()方法的類的實(shí)例不應(yīng)該作為字典的key(強(qiáng)行作為key不會(huì)報(bào)錯(cuò),但是改變實(shí)例的屬性值會(huì)導(dǎo)致找不到key對(duì)應(yīng)的value),key的哈希值必須唯一不可變,key的hash值改變會(huì)導(dǎo)致找不到key對(duì)應(yīng)的value。
原文鏈接:https://blog.csdn.net/lnotime/article/details/81194962
相關(guān)推薦
- 2022-11-05 nginx修改默認(rèn)端口方法圖文詳解_nginx
- 2023-10-12 nvm的作用、下載、使用、以及Mac使用時(shí)遇到commond not found:nvm如何解決
- 2022-07-11 Python字節(jié)碼與程序執(zhí)行過(guò)程詳解_python
- 2022-07-18 數(shù)據(jù)結(jié)構(gòu) III 深入理解棧和隊(duì)列實(shí)現(xiàn)
- 2022-05-07 LINQ教程之使用Lambda表達(dá)式_實(shí)用技巧
- 2023-01-02 Kotlin類對(duì)象class初始化與使用_Android
- 2022-08-25 python數(shù)學(xué)建模(SciPy+?Numpy+Pandas)_python
- 2022-06-19 selenium?IDE自動(dòng)化測(cè)試腳本的實(shí)現(xiàn)_其它綜合
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支