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

