python字典和集合对象可以进行索引操作_Python中的字典跟集合整理笔记
泛映射類型
映射類型:不僅僅是dict,標準庫里的所有映射類型都是利用dict來實現的,因此它們有個共同的限制,即只有可散列的數據類型才能用做這些映射的鍵。(只有鍵有這個需求,值并不需要必須是可散列的數據類型。)
什么是可散列的數據類型?
可散列的對象在它的生命周期中,散列值是不變的,需要實現__hash__()方法。另外散列對象還要有__eq__()方法,這樣才能跟其他鍵作比較。 - 原子不可變數據類型都是可散列的(str,bytes和數值類型,frozenset) -?dict,list是不可散列的
用setdefault處理找不到的鍵
當字典d[k]找不到值會拋出異常,通常我們使用d.get(k,default)來代替d[k],給找不到的鍵默認一個返回值。 但是要更新某個鍵對應的值的時候,不管是用__getitem__還是get都不太自然的,效率很低。
# 這樣使用 setdefaultmy_dict.setdefault(key,[]).append(new_value)# 跟這樣寫使用默認的dictif key not in my_dict:
my_dict[key]=[]my_dict[key].append(new_value)
兩者的效果是一樣的,只不過后者至少要進行兩次查詢——如果鍵不存在的話,就是三次,使用setdefault只需要一次就可以完成整個操作。
映射的彈性查詢
所謂的彈性查詢就是,我找的鍵不在映射里面存在的時候,也能返回一個默認值比如
d.get(key,default)
python有兩個途徑達到整個目的, - 一個是通過defaultdict這個類型而不是普通的dict?- 另一個是給自己頂一個dict的子類,然后在子類中實現__missing__方法。
defaultdict:處理找不到的鍵的一個選擇
dd = defaultdict(list) print(dd['new-key']) # []"""調用list()來建立一個列表。把這個新列表作為值,'new-key'作為它的鍵,放到dd中。返回這個列表的引用。"""print(dd) #defaultdict(, {'dddd': []})
注意如果在創建defaultdict的時候沒有指定default_factory,查詢不存在鍵會觸發KeyError
特殊方法__missing__
所有的映射類型找不到鍵的時候,都會使用到__missing__方法。 雖然基類dict并沒有定義這個方法,但是dict知道有這么個東西的存在。 也就是說,如果有一個類繼承了dict,然后這個繼承類提供了__missing__方法, 那么在__getitem__碰到找不到鍵的時候,python會自動調用它,而不是拋出一個KeyError異常。?__missing__方法只會被__getitem__調用
字典的變種
collections.OrderedDict:這個類型在添加鍵的時候會保持順序,因此鍵的迭代次序總是一致的。
collections.ChainMap:該類型可以容納數個不同的對象,然后在進行鍵查找操作的時候,這些對象會被當做一個整體逐個被查找。
import collections# 初始化字典dict1 = {'a': 1, 'b': 2}dict2 = {'b': 3, 'c': 4}# 初始化ChainMapchain = collections.ChainMap(dict1, dict2)# 使用maps輸出chainMapprint(chain.maps) ?# [{'b': 2, 'a': 1}, {'b': 3, 'c': 4}]# 輸出keyprint(list(chain.keys())) ?# ['b', 'c', 'a']# 輸出值print(list(chain.values())) ?# [2, 4, 1]# 訪問print(chain['b']) ?# 2print(chain.get('b')) ?# 2# 使用new_child添加新字典dict3 = {'f': 5}new_chain = chain.new_child(dict3)print(new_chain.maps) ?# [{'f': 5}, {'b': 2, 'a': 1}, {'b': 3, 'c': 4}]reversed(new_chain.maps)print(new_chain.maps)
collections.Counter:這個映射類型會給鍵準備一個整數計數器。每次更新一個鍵的時候都會增加這個計數器。
collections.UserDict:把標準的dict用純python又實現了一遍。
不可變的映射類型
標準庫里所有的映射類型都是可變的,如果遇到不能讓用戶錯誤的修改某個映射。 使用types.MappingProxyType,如果給這個類一個映射,它會返回一個只讀的映射視圖(動態的)。
集合
相對dict,set這個概念在python算是比較年輕的,有set 跟 frozenset?集合的本質是許多唯一對象的聚集,所以集合中的元素必須都是可散列的,set類型本身是不可散列的,但是frozenset時可散列的。 集合可以進行中綴運算符。
dict和set的背后python里的dict和set的效率有多高?
為什么它們是無序的?
為什么并不是所有的python對象都可以當做dict的鍵或者是set的元素?
為什么dict的鍵和set元素的順序是根據他們被添加的次序而定的,以及為什么在映射對象的生命周期中,這個順序是一成不變的?
為什么不應該在迭代循環dict或者是set的同時往里添加元素?
字典中的散列表
散列表其實是一個稀疏數組(總是有空白元素的數組成為稀疏數組)?散列表里的單元通常叫做表元(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的實現及其導致的結果
散列表帶給dict的優勢和限制。
鍵必須是可散列的
一個可散列的對象必須滿足以下的需求: - 支持hash()函數,并且通過__hash__()方法所得到的散列值是不變的。 - 支持通過__eq__()方法來檢測相等性。 - 若?a == b為真,則hash(a) == hash(b)也為真。
用戶自定義的對象默認是可散列的,它們的散列值有id()來獲取。
字典在內存上面開銷巨大
通常不需要優化,如果數據量巨大考慮使用tuple()來替代dict()
特殊方法__slots__
鍵查詢很快
dict的實現是典型的空間換時間
鍵的次序取決于添加順序
當往dict中添加新建而又發生散列沖突的時候,新建可能會被安排存放在另個一個位置。
dict([key1,value1],[key2,value2]) == dict([key2,value2],[key1,value1]) # true
雖然鍵的次序是亂的,但是被視作相等的。這就是為什么說字典是無序的原因。
往字典中添加新建可能會改變已有鍵的順序
無論何時往字典添加新的鍵,Python的解釋器都可能做出為字典擴容的決定。 擴容導致的結果是需要一個更大散列表,并把字典里已有的元素添加到新表里,這個過程就可能出現散列沖突,導致新的散列表中的鍵的次序變化。
所以不要對字典同時迭代和修改。?python3中的.keys() .items() 和.values()方法返回都是字典的視圖,這些方法返回的值更像是集合。
set的實現以及導致的結果可以參照沒有值dict
總結
以上是生活随笔為你收集整理的python字典和集合对象可以进行索引操作_Python中的字典跟集合整理笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中北大学计算机二级负责老师,导师信息#中
- 下一篇: JAVA进阶教学之(Object类中的h