【Python】詳解 collections.defaultdict

目錄

一、緒論

二、defaultdict 類

2.1 說明

2.2 用例

2.2.1 整合字典

2.2.2 計數字典

2.2.3 不重複計數字典

2.2.4 更靈活的方式 —— 使用 lambda 提供各種類型的默認 value


一、緒論

collections 作爲 Python 的內建集合模塊,實現了許多十分高效的特殊容器數據類型,即除了 Python 通用內置容器: dict、list、set 和 tuple 等的替代方案。在 IDLE 輸入 help(collections) 可查看幫助文檔,其中常見的類/函數如下:

名稱 功能
namedtuple 用於創建具有命名字段的 tuple 子類的 factory 函數 (具名元組)
deque 類似 list 的容器,兩端都能實現快速 append 和 pop (雙端隊列)
ChainMap 類似 dict 的類,用於創建多個映射的單視圖
Counter 用於計算 hashable 對象的 dict 子類 (可哈希對象計數)
OrderedDict 記住元素添加順序的 dict 子類 (有序字典)
defaultdict dict 子類調用 factory 函數來提供缺失值
UserDict 包裝 dict 對象以便於 dict 的子類化
UserList 包裝 list 對象以便於 list 的子類化
UserString 包裝 string 對象以便於 string 的子類化

而本文詳述的對象爲默認字典 —— defaultdict 。 


二、defaultdict 類

2.1 說明

class collections.defaultdict([default_factory[, ...]]) 

defaultdict,顧名思義是默認字典,它返回一個類似 dict 的新對象。defaultdict 作爲內置 dict 類的子類,重載了一個新方法並添加了一個可寫的實例變量,而其餘功能則與 dict 類相同。

第一個參數 default_factory工廠函數 (factory function),默認爲 None,可選 list、tuple、set 、str 、int 等用於創建各種類型對象的內建函數 (工廠),其作用在於:當用 defaultdict 創建的默認字典的 key 不存在時,將返工廠函數 default_factory 的默認值。例如,list 對應 [ ]、tuple 對應 ()。而其他參數用法則同 dict,包括關鍵詞參數。

通常,我們創建 dict 時,若索引了不存在的 key,則會導致 KeyError

>>> dict0 = {}
>>> dict0[0]
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    dict0[0]
KeyError: 0

爲避免這樣的問題,可以對默認字典 defaultdict  饋入指定“工廠” 作爲默認工廠,當訪問不存在的 key 時,會調用默認工廠自動創建並返回一個默認 value,而非 KeyError,例如:

>>> from collections import defaultdict

## dict 初始化之初, key=0 原本是不存在的,
## 但在 defaultdict 的作用下, 根據工廠函數構建了相應的 默認 value
# -------------------------------------------------------------
>>> dict1 = defaultdict(list)
>>> dict1
defaultdict(<class 'list'>, {})
>>> dict1[0]  
[]
>>> dict1
defaultdict(<class 'list'>, {0: []})  
# -------------------------------------------------------------
>>> dict2 = defaultdict(tuple)
>>> dict2
defaultdict(<class 'tuple'>, {})
>>> dict2[0]
()
>>> dict2
defaultdict(<class 'tuple'>, {0: ()})
# -------------------------------------------------------------
>>> dict3 = defaultdict(set)
>>> dict3
defaultdict(<class 'set'>, {})
>>> dict3[0]   # 注意, 爲區別 dict 的 {}, set 創建的是 set()
set()
>>> dict3
defaultdict(<class 'set'>, {0: set()})    
# -------------------------------------------------------------
>>> dict4 = defaultdict(str)
>>> dict4
defaultdict(<class 'str'>, {})
>>> dict4[0]
''
>>> dict4
defaultdict(<class 'str'>, {0: ''})
# -------------------------------------------------------------
>>> dict5 = defaultdict(int)
>>> dict5
defaultdict(<class 'int'>, {})
>>> dict5[0]
0
>>> dict5
defaultdict(<class 'int'>, {0: 0})
# -------------------------------------------------------------
>>> dict6 = defaultdict(float)
>>> dict6
defaultdict(<class 'float'>, {})
>>> dict6[0]
0.0
>>> dict6
defaultdict(<class 'float'>, {0: 0.0})

注意,上述例子的實參是 list、tuple、set、str、int、float,它們均爲類對象,而非新列表 list()、新元組 tuple() 等等。

當然,形參工廠函數 default_factory 可接受的工廠實參遠不止這些。

但是,若實例化 defaultdict 對象時不指定 default_factory 參數,則默認爲 None,使之與 dict 基本沒有區別,訪問不存在的 key 時同樣會拋出 KeyError,而不會創建默認 value (因爲沒有默認工廠可用!):

>>> dict00 = defaultdict()
>>> dict00
defaultdict(None, {})
>>> dict00[0]

2.2 用例

2.2.1 整合字典

例如,使用 defaultdict 能夠很容易地將 tuple-list 整合爲 value 爲 list 的 dict,而不必預先檢查 key 的存在性:

>>> old = [('yellow', 2), ('blue', 4), ('yellow', 1), ('blue', 1), ('red', 2)]
>>> new = defaultdict(list)
>>> for key, value in old:
	new[key].append(value)

>>> new.items()
dict_items([('yellow', [2, 1]), ('blue', [4, 1]), ('red', [2])])
>>> sorted(new.items())
[('blue', [4, 1]), ('red', [2]), ('yellow', [2, 1])]

使用了 defaultdict 的方法比使用 dict.setdefault() 的等價方法更加簡潔高效:

>>> old = [('yellow', 2), ('blue', 4), ('yellow', 1), ('blue', 1), ('red', 2)]
>>> new2 = {}
>>> for key, value in old:
	new2.setdefault(key, []).append(value)

>>> new2.items()
dict_items([('yellow', [2, 1]), ('blue', [4, 1]), ('red', [2])])
>>> sorted(new2.items())
[('blue', [4, 1]), ('red', [2]), ('yellow', [2, 1])]

2.2.2 計數字典

例如,使用 defaultdict 爲 string 中各元素出現次數計數:

>>> string = 'nobugshahaha'
>>> count = defaultdict(int)
>>> for key in string:
	count[key] += 1

>>> count.items()
dict_items([('n', 1), ('o', 1), ('b', 1), ('u', 1), ('g', 1), ('s', 1), ('h', 3), ('a', 3)])
>>> sorted(count.items())
[('a', 3), ('b', 1), ('g', 1), ('h', 3), ('n', 1), ('o', 1), ('s', 1), ('u', 1)]

但對於計數,我還是傾向於使用專業的計數器類 collections.Counter()

from collections import Counter
>>> count2 = Counter(string)
>>> count2
Counter({'h': 3, 'a': 3, 'n': 1, 'o': 1, 'b': 1, 'u': 1, 'g': 1, 's': 1})
>>> count2.items()
dict_items([('n', 1), ('o', 1), ('b', 1), ('u', 1), ('g', 1), ('s', 1), ('h', 3), ('a', 3)])
>>> count2.most_common()
[('h', 3), ('a', 3), ('n', 1), ('o', 1), ('b', 1), ('u', 1), ('g', 1), ('s', 1)]

2.2.3 不重複計數字典

使用 defaultdict 構造 set-dict 實現元素的不重複計數:

>>> colors = [('yellow', 2), ('blue', 4), ('yellow', 1), ('blue', 4), ('yellow', 2)]
>>> color_set = defaultdict(set)
>>> for key, value in colors:
	color_set[key].add(value)
	
>>> color_set.items()
dict_items([('yellow', {1, 2}), ('blue', {4})])
>>> sorted(color_set.items())
[('blue', {4}), ('yellow', {1, 2})]

2.2.4 更靈活的方式 —— 使用 lambda 提供各種類型的默認 value

注意這裏面的用法有一點點高級,有興趣者看:

>>> def constant_factory(value):
    ''' 使用 lambda 函數提供任何常量值 '''
	return lambda: value    # 不常見用法

>>> words = defaultdict(constant_factory('~<China>~'))
>>> words
defaultdict(<function constant_factory.<locals>.<lambda> at 0x0000025698DF37B8>, {})

>>> words.items()
dict_items([])
>>> words.update(name='Liangzai', action='ran')
>>> words.items()
dict_items([('name', 'Liangzai'), ('action', 'ran')])

>>> "%(name)s %(action)s to %(object)s" % words    # 不常見用法
'Liangzai ran to ~<China>~'

參考文獻:

Think Python 2 Edition》—— Allen B. Downey

https://docs.python.org/zh-cn/3.6/library/collections.html?highlight=defaultdict#collections.defaultdict

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章