網站首頁 編程語言 正文
Python字典的key都可以是什么
答
一個對象能不能作為字典的key,就取決于其有沒有__hash__方法。所以所有python自帶類型中,除了list、dict、set和內部至少帶有上述三種類型之一的tuple之外,其余的對象都能當key。
比如數值/字符串/完全不可變的元祖/函數(內建或自定義)/類(內建或自定義)/方法/包等等你能拿出手的,不過有的實際意義不高。還有數值型要注意,因為兩個不同的相等數字可以有相同的哈希值,比如1和1.0。
解釋
代碼版本:3.6.3;文檔版本:3.6.6
Unlike sequences, which are indexed by a range of numbers, dictionaries are indexed by keys, which can be any immutable type; strings and numbers can always be keys. Tuples can be used as keys if they contain only strings, numbers, or tuples; if a tuple contains any mutable object either directly or indirectly, it cannot be used as a key. You can’t use lists as keys, since lists can be modified in place using index assignments, slice assignments, or methods like append()and extend().
?字典的鍵可以是任意不可變類型,需要注意的是tuple元組作為鍵時,其中不能以任何方式包含可變對象。
?那。。到底什么樣的是不可變類型呢?不可能給對象專門標注一個屬性是可變類型還是不可變類型啊,這沒有任何其他意義,一定是通過其他途徑實現的。把list當做鍵試一下
a = [1, 2, 3]
d = {a: a}
?
?
# 第二行報錯:
# TypeError: unhashable type: 'list'
報錯說list類型是不可哈希的,噢,原來是靠能不能hash來判斷的,另外文檔下面接著說同一字典中每個鍵都是唯一的,正好每個對象的哈希值也是唯一的,對應的很好。
It is best to think of a dictionary as an unordered set of key: value pairs, with the requirement that the keys are unique (within one dictionary).
查看源代碼可以看到object對象是定義了__hash__方法的,
而list、set和dict都把__hash__賦值為None了
# 部分源碼
?
class object:
? ? """ The most base type """
?
? ? def __hash__(self, *args, **kwargs): ?# real signature unknown
? ? ? ? """ Return hash(self). """
? ? ? ? pass
?
?
class list(object):
? ? __hash__ = None
?
?
class set(object):
? ? __hash__ = None
?
?
class dict(object):
? ? __hash__ = None
那這樣的話。。。我給他加一個hash不就能當字典的key了,key不就是可變的了。
注意
此處只是我跟著想法隨便試,真的應用場景不要用可變類型作為字典的key。
class MyList(list):
? ? """比普通的list多一個__hash__方法"""
?
? ? def __hash__(self):
? ? ? ? # 不能返回hash(self)
? ? ? ? # hash(self)會調用self的本方法,再調用回去,那就沒完了(RecursionError)
? ? ? ? # 用的時候要注意實例中至少有一個元素,不然0怎么取(IndexError)
? ? ? ? return hash(self[0])
?
?
l1 = MyList([1, 2]) ?# print(l1) -> [1, 2]
d = {l1: 'Can?'}
print(d) ?# --> ?{[1, 2]: 'Can?'}
l1.append(3)
print(d) ?# {[1, 2, 3]: 'Can?'}
print(d[l1]) ?# --> ?Can?
到這里就可以肯定的說,一個對象能不能作為字典的key,就取決于其有沒有__hash__方法。所以所有python自帶類型中,目前我已知的除了list、dict、set和內部帶有以上三種類型的tuple之外,其余的對象都能當key。而我們自己定義的類,一般情況下都直接間接的和object有關,都帶有__hash__方法。
另外我想到,既然字典的鍵是唯一的,而哈希值也是唯一的,這么巧,鍵的唯一性不會就是用哈希值來確定的吧?我上一個例子中__hash__方法返回的是0號元素的哈希值,那我直接用相同哈希值的對象是不是就能改變那本來不屬于它的字典值呢?
class MyList(list):
? ? def __hash__(self):
? ? ? ? return hash(self[0])
?
?
l1 = MyList([1, 2]) ?# print(l1) -> [1, 2]
d = {}
d[l1] = l1
print(d) ?# {[1, 2]: [1, 2]}
d[1] = 1
print(d) ?# {[1, 2]: [1, 2], 1: 1}
竟然沒有改成功而是新添加了一個鍵值對,可self[0]就是1啊,哈希值一樣啊,怎么會不一樣呢?難道要鍵的值一樣才能判斷是同一個鍵嗎?重寫__eq__方法試一下。
class MyList(list):
? ? def __hash__(self):
? ? ? ? return hash(self[0])
?
? ? def __eq__(self, other):
? ? ? ? return self[0] == other
?
?
l1 = MyList([1, 2]) ?# print(l1) -> [1, 2]
d = {}
d[l1] = l1
print(d) ?# {[1, 2]: [1, 2]}
d[1] = 1
print(d) ?# {[1, 2]: 1}
這回成功了,那就是__hash__返回值相等,且eq判斷也相等,才會被認為是同一個鍵。那這兩個先判斷哪個呢?加個代碼試一下
class MyList(list):
? ? def __hash__(self):
? ? ? ? print('hash is run')
? ? ? ? return hash(self[0])
?
? ? def __eq__(self, other):
? ? ? ? print('eq is run')
? ? ? ? return self[0] == other
?
?
l1 = MyList([1, 2]) ?# print(l1) -> [1, 2]
d = {}
d[1] = 1
d[l1] = 'l1'
print(d)
?
?
# 結果:
# hash is run
# eq is run
# {1: 'l1'}
__hash__先執行,另外字典在內存中存儲數據的位置和鍵的hash也是有關的,邏輯上也像印證。
先計算hash,找到相對應的那片內存空間,里面沒有值的話就直接寫入,對于字典來說就是新增鍵值對;如果里面已經有值了,那就判斷新來的鍵和原來的那里的鍵是不是相等,相等就認為是一個鍵,對于字典來說就是更新值,不相等就再開空間,相當于字典新增鍵值對。
在你驗證自己想法的時候可能遇到__hash__和__eq__的一些想不到的麻煩,可以看這里:__hash__和__eq__的繼承使用問題
原文鏈接:https://blog.csdn.net/lnotime/article/details/81192207
相關推薦
- 2022-04-11 蝴蝶優化算法及實現源碼_相關技巧
- 2022-06-30 深度卷積神經網絡各種改進結構塊匯總_其它綜合
- 2022-07-23 C#操作windows系統進程的方法_C#教程
- 2022-08-13 oracle利用sql語句實現分組小計(grouping,group by ,rollup)
- 2022-08-20 C#使用對象序列化類庫MessasgePack_C#教程
- 2022-05-27 .Net動態生成controller遇到的坑_實用技巧
- 2022-10-29 C語言實現單元測試的示例詳解_C 語言
- 2022-05-06 resty更新header控制api版本數據源讀寫分離_其它綜合
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支