dict 的高性能與其存儲方式是分不開的,咱們知道 dict 的存儲是基於哈希表(又稱散列表),須要計算 hash 值,那麼是計算誰的 hash 值呢?是像別人說的:存儲 dict 元素前計算 key 的 hash 值?python
這裏先建立個字典segmentfault
>>> my_dict = {'a': 'apple', 'b': 'banana'}
因爲哈希表是一塊連續的內存空間(數組),在不考慮 hash 值衝突的狀況下,若是計算的是 key 的 hash 值,那麼:'a'
的 hash 值與 'b'
的 hash 值之間的差值 與 'a'
的內存地址與 'b'
的內存地址之間的差值(可理解爲內存地址裏的距離) 相等纔對,也就是說如下的等式成立纔對數組
hash('a') - hash('b') == id('a') - id('b')
但事實上面等式返回的是 False
app
>>> hash('a') - hash('b') == id('a') - id('b') False
先看看其中各項的具體值是多少性能
>>> hash('a') -7336862871683211644 >>> hash('b') 3607308758832868774 >>> id('a') 1290454097736 >>> id('b') 1290454096056
>>> id('a') - id('b') 1680 >>> hash('a') - hash('b') -10944171630516080418
能夠很明顯得看到差距仍是挺大的
這說明計算的不是 key 的 hash 值(這種說法不夠嚴謹),那計算的是什麼呢?code
在不考慮 hash 衝突的狀況下, 'a'
所在內存地址的 hash 值與 'b'
所在內存地址的 hash 值之間的差值 和 'a'
的內存地址與 'b'
的內存地址之間的差值 相等,也就是說如下的等式成立纔對對象
hash(id('a')) - hash(id('b')) == id('a') - id('b')
>>> hash(id('a')) - hash(id('b')) == id('a') - id('b') True >>> id('a') - id('b') 1680 >>> hash(id('a')) - hash(id('b')) 1680
下面再多驗證幾個內存
>>> my_dict['c'] = 'cherry' >>> hash(id('b')) - hash(id('c')) == id('b') - id('c') True >>> id('b') - id('c') 791760 >>> hash(id('b')) - hash(id('c')) 791760
>>> a['d'] = 'date' >>> hash(id('d')) - hash(id('c')) == id('d') - id('c') True >>> id('d') - id('c') 1400 >>> hash(id('d')) - hash(id('c')) 1400
到這裏就能夠證實上面的結論get
好比上面的'a'
(1 個字符) 明顯比其所在的內存地址 1290454097736
(13 個字符)要短。短的計算不是更快嗎?
記住一句話:Python 中一切皆對象,'a'
是個 str 對象,1290454097736
是個 int 對象hash
>>> type('a') <class 'str'> >>> type(id('a')) <class 'int'>
一個對象裏不是僅僅存儲對應值,它還有不少屬性(含方法),來看看誰的屬性多
>>> len(dir('a')) 77 >>> len(dir(id('a'))) 70
str 對象比 int 對象多 7 個屬性
它們都有個叫 __sizeof__()
的魔法方法,用於獲取當前對象所佔用的內存空間大小(字節)
>>> id('a').__sizeof__() 32 >>> 'a'.__sizeof__() 50
從上面能夠發現:雖然 'a'
看起來只有 1 個字符,但其佔用的內存空間要大於其內存地址 id('a')
所佔用的空間
固然這不是主要緣由,Python 解釋器會將其轉換爲適當的數據類型再進行 hash 計算
不過,dict 的 key 不單單能夠是 str 對象,也能夠是 int、bytes、fromzenset 等這些可哈希(hashable
)對象,可哈希對象都是不可變(immutable
)對象(注意:反之不必定成立,如 tuple),不可變對象內存地址不變。大多數狀況下,相比計算這些不一樣對象類型的 hash 值,直接計算對象所在內存地址(整數)的 hash 值性能更高,這也就是爲何不是計算 key 的 hash 值,而是計算 key 所在內存地址的 hash 值