字典的變種
不可變的對映型別
集合`dict`和`set`的背後
`dict`的實現及其導致的結果
set的實現以及導致的結果可以參照沒有值dict
對映型別:不僅僅是dict
,標準庫里的所有對映型別都是利用dict
來實現的,因此它們有個共同的限制,即只有可雜湊的資料型別才能用做這些對映的鍵。(只有鍵有這個需求,值並不需要必須是可雜湊的資料型別。)
什麼是可雜湊的資料型別?
可雜湊的物件在它的生命週期中,雜湊值是不變的,需要實現__hash__()
方法。另外雜湊物件還要有__eq__()
方法,這樣才能跟其他鍵作比較。
當字典d[k]
找不到值會丟擲異常,通常我們使用d.get(k,default)
來代替d[k]
,給找不到的鍵預設乙個返回值。
但是要更新某個鍵對應的值的時候,不管是用__getitem__
還是get
都不太自然的,效率很低。
# 這樣使用 setdefault
my_dict.setdefault(key,
)# 跟這樣寫使用預設的dict
if key not
in my_dict:
my_dict[key]=[
]my_dict[key]
兩者的效果是一樣的,只不過後者至少要進行兩次查詢——如果鍵不存在的話,就是三次,使用setdefault
只需要一次就可以完成整個操作。
所謂的彈性查詢就是,我找的鍵不在對映裡面存在的時候,也能返回乙個預設值比如
d.get(key,default)
python
有兩個途徑達到整個目的,
dd = defaultdict(
list
)print
(dd[
'new-key'])
# """
呼叫list()來建立乙個列表。
把這個新列表作為值,'new-key'作為它的鍵,放到dd中。
返回這個列表的引用。
"""print
(dd)
#defaultdict(, )
注意如果在建立defaultdict
的時候沒有指定default_factory
,查詢不存在鍵會觸發keyerror
所有的對映型別找不到鍵的時候,都會使用到__missing__
方法。
雖然基類dict
並沒有定義這個方法,但是dict
知道有這麼個東西的存在。
也就是說,如果有乙個類繼承了dict
,然後這個繼承類提供了__missing__
方法,
那麼在__getitem__
碰到找不到鍵的時候,python
會自動呼叫它,而不是丟擲乙個keyerror
異常。
__missing__
方法只會被__getitem__
呼叫
collections.ordereddict
:這個型別在新增鍵的時候會保持順序,因此鍵的迭代次序總是一致的。
collections.chainmap
:該型別可以容納數個不同的物件,然後在進行鍵查詢操作的時候,這些物件會被當做乙個整體逐個被查詢。
import collections
# 初始化字典
dict1 =
dict2 =
# 初始化chainmap
chain = collections.chainmap(dict1, dict2)
# 使用maps輸出chainmap
print
(chain.maps)
# [, ]
# 輸出key
print
(list
(chain.keys())
)# ['b', 'c', 'a']
# 輸出值
print
(list
(chain.values())
)# [2, 4, 1]
# 訪問
print
(chain[
'b']
)# 2
print
(chain.get(
'b')
)# 2
# 使用new_child新增新字典
dict3 =
new_chain = chain.new_child(dict3)
print
(new_chain.maps)
# [, , ]
reversed
(new_chain.maps)
print
(new_chain.maps)
collections.counter
:這個對映型別會給鍵準備乙個整數計數器。每次更新乙個鍵的時候都會增加這個計數器。
collections.userdict
:把標準的dict
用純python
又實現了一遍。
相對dict
,set
這個概念在python
算是比較年輕的,有set 跟 frozenset
集合的本質是許多唯一物件的聚集,所以集合中的元素必須都是可雜湊的,set
型別本身是不可雜湊的,但是frozenset
時可雜湊的。
集合可以進行中綴運算子。
雜湊表其實是乙個稀疏陣列(總是有空白元素的陣列成為稀疏陣列)
雜湊表裡的單元通常叫做表元(bucket),在dict
的雜湊表中每個鍵值對都占用乙個表元,每個表元分都有兩個部分,乙個是對鍵的引用,乙個是對值的引用。
如果把物件放到雜湊表,那麼首先要計算這個元素鍵的雜湊值,python
中可以用hash()
方法來做這個事情。
雜湊值和相等性
如果1==1.0
為真,那麼`hash(1)==hash(1.0)也必須為真
雜湊表演算法
為了獲取my_dict[search_key]
背後的值,python首先呼叫hash(search_key)
來計算search_key
的雜湊值,把這個值最低的幾位數字當做偏移量,在雜湊表裡查詢表元(具體取幾位,得看當前雜湊表的大小)。
若找到的表元為空的,則丟擲keyerror
異常。若不為空的,則表元裡會有一對found_key:found_value
。
這時候python
會檢驗search_key == found_key
是否為真,如果它們相等,就回返回found_value
。
如果search_key
和found_key
不匹配的話,這種情況稱為雜湊衝突。
發生原因是雜湊表所做的其實是把隨機的元素對映到只有幾位的數字上,而雜湊表本身的索引又只依賴於這個數字的一部分。
為了解決雜湊衝突,演算法會在雜湊值中另外再取幾位,然後用特殊方法處理一下,把新的到的資料在當做索引來尋找表元。
問題:如果定位乙個表元?[^2]
雜湊表帶給dict
的優勢和限制。
鍵必須是可雜湊的
乙個可雜湊的物件必須滿足以下的需求:
使用者自定義的物件預設是可雜湊的,它們的雜湊值有id()
來獲取。
字典在記憶體上面開銷巨大
通常不需要優化,如果資料量巨大考慮使用tuple()
來替代dict()
特殊方法__slots__
鍵查詢很快
dict
的實現是典型的空間換時間
鍵的次序取決於新增順序
當往dict
中新增新建而又發生雜湊衝突的時候,新建可能會被安排存放在另個乙個位置。
dict
([key1,value1]
,[key2,value2])==
dict
([key2,value2]
,[key1,value1]
)# true
雖然鍵的次序是亂的,但是被視作相等的。這就是為什麼說字典是無序的原因。
往字典中新增新建可能會改變已有鍵的順序
無論何時往字典新增新的鍵,python的直譯器都可能做出為字典擴容的決定。
擴容導致的結果是需要乙個更大雜湊表,並把字典裡已有的元素新增到新表裡,這個過程就可能出現雜湊衝突,導致新的雜湊表中的鍵的次序變化。
所以不要對字典同時迭代和修改。
python3
中的.keys() .items() 和.values()
方法返回都是字典的檢視,這些方法返回的值更像是集合。
python中遞迴,字典,集合
遞迴 函式呼叫自身的行為或過程,如斐波那契數列的生成 如下 值得注意是,有時迭代演算法與遞迴演算法,遞迴演算法並不是很好,反而其執行速度慢 字典 字典在python中屬於一種對映型別而不是序列型別,主要特徵是用大括號括起來,主要特徵引數是鍵 key 和鍵值 value 建立方法為 1 num 2 n...
元組,集合,字典筆記整理
一 1 is 和 的區別 id 是指變數在記憶體中的儲存位置 value 是指變數的值 type 是指變數的型別 當a is b 時為真時,id a id b type a type b value a value b 當a b 時為真時,value a value b type a type b ...
python 集合 字典
1.集合 建立 set 注意 建立空的集合要用set 特點 元素唯一,無序 運算 交集 並集 差集 方法 s.add x 新增單個元素 s.update 新增多個元素 s.remove 移除元素 s.clear 清空集合2.字典 建立 大括號建立字典的鍵時要加引號 dict key value 括號...