python进阶(第三章1) 字典
文章目錄
- 3.1 泛映射類型
- 什么是可散列的數據類型(鍵的要求)
- 字典的構造方法
- 3.2 字典推導(dictcomp)
- 3.3 常見的映射方法
- 用setdefault處理找不到的鍵
- 3.4 映射的彈性鍵查詢
- 3.4.1 defaultdict:處理找不到的鍵的一個選擇
- 注意:
- defaultdict與dict實例化字典類型的區別
- defaultdict的構造
- 3.4.2 特殊方法__missing__
- 3.5 字典的變種
- collections.OrderedDict (添加鍵會保持順序)
- collections.ChainMap(將多個映射合并為單個映射)
- collections.Counter
- 例子:統計單詞中各個字母出現的次數
- collections.UserDict
- 3.6 子類化UserDict
- MutableMapping.update
- Mapping.get
- 從dict或者其他內置類繼承有什么不好?
- 3.7 不可變映射類型(動態的只讀的映射視圖:MappingProxyType)
3.1 泛映射類型
collections.abc模塊有Mapping和MutableMapping 這兩個抽象基類,它們的作用是為了dict和其他類似的類型定義形式接口,然后非抽象映射類型一般不會直接繼承這些抽象基類,它們會直接對dict或者collections.UserDict進行擴展。這些抽象基類的主要作用是作為形式化的文檔,它們定義了構建一個映射類型所需要的最基本的接口。然后它們還可以跟isinstance一起被用來判定某個數據是不是廣義上的映射類型:
>>> from collections import abc >>> my_dict={} # 字典是典型的鍵值對 >>> isinstance(my_dict,abc.Mapping) True >>> isinstance([1, 2], abc.Mapping) False #列表時序列 >>> isinstance((1, 2), abc.Mapping) False #元組也是序列 >>> isinstance('sdbd', abc.Mapping) False #字符串也是序列- 這里用isintance而不是type來檢查某個參數是否為dict類型,因為這個參數有可能不是dict,而是一個比較另類的映射類型。(這句話不太明白)
- 標準庫里的所有映射類型都是利用dict來實現的,因此它們有個共同的限制,即只有可散列的數據類型才可以用作這些映射里的鍵(只有鍵有這個要求,值沒有此要求)
什么是可散列的數據類型(鍵的要求)
如果一個對象是可散列的,那么在這個對象的生命周期中,它的散列值是不變的而且這個對象需要實現__hash__()方法。另外可散列對象還要有__eq__()方法,這樣才能和其他鍵作比較。如果兩個散列對象是相等的,那么它們的散列值一定是一樣
的。
可散列類型包括:
- (1)原子不可變類型(str, bytes和數值類型)
- (2)frozenset
- (3)元組:只有當元組包含的所有元素都是可散列的情況下。
可以用句話說:python里所有的不可變類型都是可散列的
一般來講用戶自定義的類型的對象都是可散列的,散列值就是它們的id()函數的返回值。
字典的構造方法
>>> a= dict(one=1,two=2,three=3) >>> b={'one':1,'two':2,'three':3} >>> c= dict(zip(['one','two','three'],[1,2,3])) >>> d = dict({'one':1,'two':2,'three':3}) >>> e=dict([('two',2),('one',1),('three',3)]) >>> a == b ==c == d == e True- 注意這里的相等,只不過是值相等,但是不同的對象
3.2 字典推導(dictcomp)
列表生成器和生成器表達式的概念已經移植到了字典上,從而有了字典推導。
字典推導可以從任何鍵值對作為元素的可迭代對象中構建出字典。
例子:
字典推導的表達式會蔓延到其他數據結構類型
3.3 常見的映射方法
除了
- dict
- defaultdict
- OrderedDict
這三種常見方法
在映射對象的方法里,setdefault可能是比較微妙的一個。盡管用的次數不多,但是它一旦發揮作用,就可以節省不少次鍵查詢,讓程序更高效。
用setdefault處理找不到的鍵
我們可以使用d.get(k,default)來代替d[k],給找不到的鍵一個默認的返回值(這比處理keyError方便不少)
看個例子:
例子2:(使用dict.setdefault()方法來設置默認值,統計字符串出現的次數)
strings = ('puppy', 'kitten', 'puppy', 'puppy','weasel', 'puppy', 'kitten', 'puppy') counts = {} for kw in strings:counts.setdefault(kw, 0)counts[kw] += 1dict.setdefault()方法的返回值可以重寫for循環中的代碼,使其更加簡潔:
strings = ('puppy', 'kitten', 'puppy', 'puppy','weasel', 'puppy', 'kitten', 'puppy') counts = {} for kw in strings:counts[kw] = counts.setdefault(kw, 0) + 13.4 映射的彈性鍵查詢
為了方便,就算某個鍵在映射里不存在,那么你也希望在通過這個鍵讀取值的時候能得到一個默認值。有兩個途徑幫我們達到這個目的。
- (1).通過defaultdict這個類型而不是普通的dict
- (2).給自己定義一個dict類型的子類,然后在這個子類中實現__missing__方法。
3.4.1 defaultdict:處理找不到的鍵的一個選擇
在用戶創建defaultdict對象的時候,就需要給它配置一個為找不到的鍵創造默認值的方法。
具體而言,在實例化一個defaultdict的時候,需要給構造方法提供一個可調用的對象,這個可調用對象會在__getitem__碰到找不到的鍵的時候被調用,讓__getitem__返回某種默認值。
比如,新建一個字典:dd=defaultdict(list),如果鍵’new-key’在dd中不存在的話,表達式dd[‘new-key’]會按照以下步驟行事。
- (1).調用list()來創建新列表
- (2).把這個新列表作為值,'new-key’作為它的鍵,放到dd中。
- (3).返回這個列表的引用
而這個用來生成默認值的可調用對象存放在名為default_factory的實例屬性里。
如果在創建defaultdict的時候沒有指定default_factory,查詢不存在的鍵會觸發KeyError.
注意:
- defaultdict里面的default_factory只會在__getitem__里被調用,在其他的方法里完全不會發揮作用。比如,dd是個defaultdict,K是個找不到的鍵,dd[k]這個表達式會調用default_factory創造某個默認值,而dd.get(k)則會返回None.
所有 這一切背后的功臣其實是特殊方法__missing__.它會在defaultdict遇到找不到的鍵的時候調用default_factory,而實際上這個特性是所有映射類型都可以去選擇的。
看個例子:
from collections import defaultdict class from_defaultdict(defaultdict):def __getitem__(self, key):return 'hello'c = from_defaultdict(list)print(c['new-key'])結果如下:
defaultdict與dict實例化字典類型的區別
使用defaultdict任何未定義的key都會默認返回一個根據method_factory參數不同的默認值, 而相同情況下dict()會返回KeyError.
比較下面代碼:
輸出:
[] Traceback (most recent call last):File "/home/maxzhang/PycharmProjects/pythoncode/t.py", line 5, in <module>print(d1['a']) KeyError: 'a'defaultdict的構造
python官方文檔中對defaultdict的定義如下:
class collections.defaultdict([default_factory[, ...]])python官方文檔中對defaultdict的解釋如下:
defaultdi: dict subclass that calls a factory function to supply missing values- default_factory 接收一個工廠函數作為參數, 例如int str,list,set等.
- defaultdict在dict的基礎上添加了一個__missing__(key)方法, 在調用一個不存的key的時候, defaultdict會調用__missing__, 返回一個根據default_factory參數的默認值, 所以不會返回Keyerror.
3.4.2 特殊方法__missing__
所有的映射類型在處理找不到的鍵的時候,都會牽扯到__missing__方法。這也是和這個方法稱作’missing’的原因。雖然基類dict并沒有定義這個方法,但是dict是知道有這么一個東西存在的。也就是說,如果有一個類繼承了dict,然后這個繼承類提供了__missing__方法,那么在__getitem__碰到找不到的鍵的時候,python會自動調用它。而不是拋出異常。
- 注意:__missing__方法只會被__getitem__調用。提供__missing__方法對get或者__contains__這些方法的使用沒有影響。
如果要自定義一個映射類型,更合適的策略是繼承collections.UserDict類。
3.5 字典的變種
collections.OrderedDict (添加鍵會保持順序)
這個類型在添加鍵的時候會保持順序,因此鍵的迭代次序總是一致的。OrderedDict的popitem方法默認刪除并返回的是字典里的最后一個元素,但是如果像my_odict.popitem(last=False)這樣調用它,那么它刪除并返回第一個被添加進去的元素。
例子:
collections.ChainMap(將多個映射合并為單個映射)
該類型可以容納數個不同的映射對象,然后在進行鍵查找操作的時候,這些對象會被當做一個整體逐個查找,直到鍵被找到為止。
例子:
collections.Counter
這個映射會給鍵準備一個整數計數器。每次更新一個鍵的時候都會增加這個基數器。所以這個類型可以用來給可散列表對象計數,或者是當成多重集來使用—多重集合就是集合里的元素可以出現不止一次。Counter實現了+和-運算符用來合并記錄,還有像most_common([n])這類很有用的方法。most_common([n])會按照次序返回映射里最常見的n個鍵和它們的計數。
例子:統計單詞中各個字母出現的次數
>>> ct = collections.Counter('afalfjlahgksdadaa') >>> ct Counter({'a': 6, 'f': 2, 'l': 2, 'd': 2, 'j': 1, 'h': 1, 'g': 1, 'k': 1, 's': 1}) >>> ct.update('aaaaaaaaadffdwe') >>> ct Counter({'a': 15, 'f': 4, 'd': 4, 'l': 2, 'j': 1, 'h': 1, 'g': 1, 'k': 1, 's': 1, 'w': 1, 'e': 1}) >>> ct.most_common(2) [('a', 15), ('f', 4)]collections.UserDict
這個類其實就是把標準dict用純python又實現了一遍
UserDict就是讓用戶繼承寫子類的。
3.6 子類化UserDict
就創造自定義映射類型來說,以UserDict為基類,總比以普通的dict為基類要來的方便。
更傾向于從UserDict而不是從dict繼承的主要原因是,后者有時會在某些方法的實現上走一些捷徑,導致我們不得不在它的子類中重寫這些方法,但是UserDict就不會帶來這些問題。
另外一個值得注意的地方是,UserDict并不是dict的子類,但是UserDict有一個叫做data的屬性是dict的實例,這個屬性就是UserDict最終存儲數據的地方。
例子:
因為UserDict 繼承的是MutableMapping,所以StrKeyDict里剩下的的那些映射類型的方法都是從UserDict,MutableMapping和Mapping這些超類繼承而來的。特別是最后的Mapping類,它雖然是一個抽象類(ABC),但是它提供了許多使用的方法。
MutableMapping.update
這個方法不但可以直接利用,它還用在__init__里,讓構造方法可以利用傳入的各種參數(其他映射類型,元素是(key,value)對的可迭代對象和鍵值參數)來新建實例。因為這個方法在背后是使用self[key]=value來添加新值的,所以它其實是在使用我們的__setitem__方法
Mapping.get
從dict或者其他內置類繼承有什么不好?
3.7 不可變映射類型(動態的只讀的映射視圖:MappingProxyType)
標準庫里所有的映射類型都是可變的,但是有時候你會有這樣的需求,比如不能讓用戶錯誤修改某個映射。
在types模塊中引入了一個封裝類名叫MappingProxyType。如果給這個類一個映射,它會返回一個動態的只讀的映射視圖。如果對原映射做出了修改,這個視圖可以觀察到,但是無法通過這個視圖對原映射進行修改。
例子:
總結
以上是生活随笔為你收集整理的python进阶(第三章1) 字典的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: git(4):git安装教程
- 下一篇: python基础 list和tuple