python 字典d[k]中key不存在的解決方案
有時候為了方便起見,就算某個鍵在映射里不存在,我們也希望在通過這個鍵讀取值的時候能得到一個默認(rèn)值
。有兩個途徑能幫我們達(dá)到這個目的,一個是通過 defaultdict
這個類型而不是普通的 dict
,另一個是給自己定義一個 dict
的子類,然后在子類中實現(xiàn)__missing__
方法。下面將介紹這兩種方法。
defaultdict:處理找不到的鍵的一個選擇
下面示例在 collections.defaultdict
的幫助下優(yōu)雅地解決了d[k]
里的問題。在用戶創(chuàng)建 defaultdict
對象的時候,就需要給它配置一個為找不到的鍵創(chuàng)造默認(rèn)值的方法。
# -*- coding: utf-8 -*- from collections import defaultdict index = defaultdict(list) # 把 list 構(gòu)造方法作為 default_factory 來創(chuàng)建一個 defaultdict print(index) # defaultdict(<class 'list'>, {}) word = 'name' # 如果 index 并沒有 word 的記錄,那么 default_factory 會被調(diào)用,為查詢不到的鍵創(chuàng)造一個值。 # 這個值在這里是一個空的列表,然后這個空列表被賦值給 index[word],繼而 # 被當(dāng)作返回值返回,因此 .append(location) 操作總能成功。 index[word].append('mmmm') print(index) # defaultdict(<class 'list'>, {'name': ['mmmm']})
具體而言,在實例化一個 defaultdict
的時候,需要給構(gòu)造方法提供一個可調(diào)用對象
,這個可調(diào)用對象會在 __getitem__
碰到找不到的鍵的時候被調(diào)用,讓 __getitem__
返回某種默認(rèn)值
。
比如,我們新建了這樣一個字典:dd = defaultdict(list)
,如果鍵 new-key
在 dd 中還不存在的話,表達(dá)式 dd['new-key']
會按照以下的步驟來行事。
- 調(diào)用
list()
來建立一個新列表 - 把這個新列表作為值,‘
new-key
’ 作為它的鍵,放到dd
中 - 返回這個列表的
引用
而這個用來生成默認(rèn)值的可調(diào)用對象存放在名為 default_factory
的實例屬性里。
注意:
如果在創(chuàng)建 defaultdict
的時候沒有指定 default_factory
,查詢不存在的鍵會觸發(fā)KeyError
。
defaultdict
里的 default_factory
只會在 __getitem__
里被調(diào)用,在其他的方法里完全不會發(fā)揮作用。比如,dd
是個 defaultdict
,k
是個找不到的鍵,dd[k]
這個表達(dá)式會調(diào)用 default_factory
創(chuàng)造某個默認(rèn)值,而 dd.get(k)
則會返回 None
。
所有這一切背后的功臣其實是特殊方法 __missing__
。它會在 defaultdict
遇到找不到的鍵的時候調(diào)用 default_factory
,而實際上這個特性是所有
映射類型都可以選擇
去支持的。
特殊方法__missing__
所有的映射類型在處理找不到的鍵
的時候,都會牽扯到 __missing__
方法。這也是這個方法稱作“missing
”的原因。雖然基類 dict
并沒有定義這個方法,但是 dict
是知道有這么個東西存在的。也就是說,如果有一個類繼承了 dict
,然后這個繼承類提供了 __missing__
方法,那么在 __getitem__
碰到找不到的鍵的時候,Python
就會自動調(diào)用它
,而不是拋出一個KeyError
異常。
注意:__missing__
方法只會被 __getitem__
調(diào)用(比如在表達(dá)式 d[k]
中)。提供__missing__
方法對 get
或者 __contains__
(in
運算符會用到這個方法)這些方法的使用沒有影響。這也是我在上面中提到,defaultdict
中的 default_factory
只對 __getitem__
有作用的原因。
如果要自定義一個映射類型,更合適的策略其實是繼承 collections.UserDict
類。這里我們從 dict
繼承,只是為了演示__missing__
是如何被 dict.__getitem__
調(diào)用的。(直接繼承dict
是有一些問題的,以后再說)
# -*- coding: utf-8 -*- class StrKeyDict0(dict): # StrKeyDict0 繼承了 dict。 def __missing__(self, key): if isinstance(key, str): # 如果找不到的鍵本身就是字符串,那就拋出 KeyError 異常。 raise KeyError(key) return self[str(key)] # 如果找不到的鍵不是字符串,那么把它轉(zhuǎn)換成字符串再進(jìn)行查找 def get(self, key, default=None): try: # get 方法把查找工作用 self[key] 的形式委托給 __getitem__,這樣在宣布查找失敗之 # 前,還能通過 __missing__ 再給某個鍵一個機(jī)會。 return self[key] except KeyError: return default # 如果拋出 KeyError,那么說明 __missing__ 也失敗了,于是返回 default。 def __contains__(self, key): # 先按照傳入鍵的原本的值來查找(我們的映射類型中可能含有非字符串的鍵),如果沒 # 找到,再用 str() 方法把鍵轉(zhuǎn)換成字符串再查找一次。 return key in self.keys() or str(key) in self.keys() if __name__ == '__main__': d = StrKeyDict0([('2', 'two'), ('4', 'four')]) print(d['2']) # two print(d[1]) # Traceback (most recent call last): # File "C:/myFiles/company_project/xbot/tests/miss_test.py", line 27, in <module> # print(d[1]) # File "C:/myFiles/company_project/xbot/tests/miss_test.py", line 8, in __missing__ # return self[str(key)] # 如果找不到的鍵不是字符串,那么把它轉(zhuǎn)換成字符串再進(jìn)行查找 # File "C:/myFiles/company_project/xbot/tests/miss_test.py", line 7, in __missing__ # raise KeyError(key) # KeyError: '1' print(2 in d) # True print(1 in d) # False
注意:
- 下面來看看為什么 isinstance(key, str) 測試在上面的 __missing__ 中是必需的。
- 如果沒有這個測試,只要 str(k) 返回的是一個存在的鍵,那么 __missing__ 方法是沒問題的,不管是字符串鍵還是非字符串鍵,它都能正常運行。但是如果 str(k) 不是一個存在的鍵,代碼就會陷入無限遞歸。這是因為 __missing__ 的最后一行中的 self[str(key)] 會調(diào)用 __getitem__,而這個 str(key) 又不存在,于是 __missing__ 又會被調(diào)用。
- 為了保持一致性,__contains__ 方法在這里也是必需的。這是因為 k in d 這個操作會調(diào)用它,但是我們從 dict 繼承到的__contains__ 方法不會在找不到鍵的時候調(diào)用 __missing__方法。__contains__ 里還有個細(xì)節(jié),就是我們這里沒有用更具 Python 風(fēng)格的方式——k in my_dict——來檢查鍵是否存在,因為那也會導(dǎo)致 __contains__ 被遞歸調(diào)用。為了避免這一情況,這里采取了更顯式的方法,直接在這個 self.keys() 里查詢。
- 像 k in my_dict.keys() 這種操作在 Python 3 中是很快的,而且即便映射類型對象很龐大也沒關(guān)系。這是因為 dict.keys() 的返回值是一個“視圖”。視圖就像一個集合,而且跟字典類似的是,在視圖里查找一個元素的速度很快。在“Dictionary view objects”(https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects)里可以找到關(guān)于這個細(xì)節(jié)的文檔。Python 2 的 dict.keys() 返回的是個列表,因此雖然上面的方法仍然是正確的,它在處理體積大的對象的時候效率不會太高,因為 k in my_list 操作需要掃描整個列表。
到此這篇關(guān)于python 字典d[k]中key不存在的解決方案的文章就介紹到這了,更多相關(guān)python 字典d[k]中key不存在內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python測試框架:pytest學(xué)習(xí)筆記
這篇文章主要介紹了Python測試框架:pytest的相關(guān)資料,幫助大家更好的利用python進(jìn)行單元測試,感興趣的朋友可以了解下2020-10-10Python獲取電腦硬件信息及狀態(tài)的實現(xiàn)方法
這篇文章主要介紹了Python獲取電腦硬件信息及狀態(tài)的實現(xiàn)方法,是一個很實用的技巧,需要的朋友可以參考下2014-08-08