開頭:讀者的“玄學”字典謎題
上周,朋友發來了一段讓他抓耳撓腮的代碼:
>>> {True: 'foo', 1: 'bar', 1.0: 'baz'}
{True: 'baz'}
“我明明定義了布爾True
、整數1
、浮點數1.0
三個鍵,結果字典里只剩True
一個鍵,值還變成了最后一個'baz'
!這是啥情況?”
這條消息讓我想起了當年自己初學 Python 時踩過的類似坑 —— 看似 “不同” 的鍵,在字典里卻被 “合并” 了。今天,咱們就用這個新例子當 “線索”,一起拆解 Python 字典的底層邏輯。
第一步:字典的“蓋樓”規則
要搞懂三個鍵為何只剩一個,得先明白字典是怎么“蓋樓”的。
簡單來說,字典的構建像搭樂高:先拼一個空架子(空字典),再按順序往架子上裝“鍵值對模塊”。上面的代碼等價于:
# 1. 拼空架子
my_dict = {}
# 2. 裝第一個模塊:鍵是True,值是'foo'
my_dict[True] = 'foo'
# 3. 裝第二個模塊:鍵是1,值是'bar'
my_dict[1] = 'bar'
# 4. 裝第三個模塊:鍵是1.0,值是'baz'
my_dict[1.0] = 'baz'
重點來了:字典的鍵是“喜新厭舊”的——如果后裝的鍵和已存在的鍵“本質相同”,就會覆蓋舊值。但問題是:True
(布爾)、1
(整數)、1.0
(浮點數)明明是三種不同的類型,怎么就“本質相同”了?
第二步:True
是“偽裝的1”
要破解“鍵相同”的謎題,得從Python的類型關系說起。
在Python的世界里,布爾(bool
)是整數(int
)的“親兒子”——官方文檔明確寫著:
“布爾類型是整數類型的子類型,
True
等價于整數1
,False
等價于整數0
。在大多數上下文中,布爾值的行為與對應的整數值一致。”
這意味著:
True == 1
→ 是真的(True
)1 == 1.0
→ 也是真的(浮點數1.0
的數值等于整數1
)- 所以
True == 1 == 1.0
→ 全等于!
用代碼驗證:
>>> True == 1
True
>>> 1 == 1.0
True
>>> True == 1 == 1.0
True
原來,在字典的“視角”里,這三個鍵根本就是“同一個人”!所以當依次插入True: 'foo'
、1: 'bar'
、1.0: 'baz'
時,后兩次插入都是在“修改同一個鍵的值”,最終只保留最后一次的'baz'
。
第三步:哈希值——字典的“身份證號”
但這里還有個疑問:就算三個鍵“數值相等”,字典怎么確定它們是“同一個鍵”?難道只看==
嗎?
這就要說到字典的底層“黑科技”——哈希表(Hash Table)。字典能快速查找鍵值對,全靠哈希值:每個鍵會先通過__hash__
方法生成一個哈希值(類似“身份證號”),字典根據這個號碼把鍵“扔”到對應的“抽屜”里;查找時,也先算哈希值,再去對應的抽屜里找。
關鍵規則是:只有當兩個鍵的哈希值相同,且==
返回True
時,字典才會認為它們是同一個鍵。
驗證這三個鍵的哈希值:
>>> hash(True)
1
>>> hash(1)
1
>>> hash(1.0)
1
三個鍵的哈希值都是1
,==
又全返回True
,字典自然把它們當同一個鍵。所以后插入的1: 'bar'
和1.0: 'baz'
,本質上都是在修改True
對應的值。
第四步:為什么鍵是True
而不是1
或1.0
?
最后一個疑問:三個鍵數值相等、哈希相同,為什么最終字典的鍵是True
,而不是后插入的1
或1.0
?
這涉及字典的“鍵保留規則”:當多個鍵被視為相同時,字典會保留第一個插入的鍵對象。比如:
>>> temp = {1.0: 'test'}
>>> temp[True] = 'update'
>>> temp
{1.0: 'update'}
這里先插入1.0
,后插入True
(與1.0
相等),字典會保留第一個鍵1.0
,并更新它的值。回到原問題,原字典第一個插入的鍵是True
,所以最終鍵是True
,值被后續插入的'bar'
和'baz'
覆蓋。
結論:三個“不同”鍵的終極真相
現在,我們可以徹底解開這個“變臉字典”的謎題了:
- 類型關系是根源:Python中
bool
是int
的子類,True
等價于1
,1
又等價于1.0
(數值相等)。 - 哈希值是身份證:三個鍵的哈希值都是
1
,字典通過“哈希值相同+==
為True”判定它們是同一個鍵。 - 先到先得保鍵形:字典保留第一個插入的鍵對象(
True
),后續插入只更新值,不修改鍵。
所以,最終結果{True: 'baz'}
的本質是:三個鍵被字典視為同一對象,后插入的值覆蓋了前值,而鍵保留了第一個插入的True
。
寫在最后:這行代碼教會我的事
這個看似“玄學”的字典表達式,其實藏著Python最核心的設計邏輯:
- 布爾類型的“隱藏身份”(
int
子類) - 字典的哈希表底層邏輯(哈希值+相等性雙重校驗)
- 鍵值對的插入順序對結果的影響
下次遇到類似“反直覺”的代碼時,別急著懷疑語言bug——打開Python解釋器,用==
和hash()
驗證一下,你會發現Python的底層邏輯遠比想象中嚴謹。
畢竟,Python的“奇怪”,往往藏著最精妙的設計。