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