はじめに
業務でPythonを書いていてdefaultdictのネスト構造を使う機会があり、その挙動を理解するのに時間がかかったため、アウトプットとして記事を書きます。
dictとdefaultdictの違いについて
dictとはKey,Valueを持った辞書構造(dictionary)型のことで、type関数で型を確認することができます。
a = {'a': 1, 'b': 2}
print(type(a))
# 出力: <class 'dict'>
dictは存在しない要素にアクセスしようとすると、エラーが発生します。
a = {'a': 1, 'b': 2}
print(a['a']) # 出力: 1
print(a['b']) # 出力: 2
# 存在しないキー
print(a['c']) # 出力: KeyError: 'c'
そこで、defaultdictを使用することでエラーを起こさずに値を生成できます。
defaultdict(int)を利用すると、存在しない要素にアクセスした時デフォルト値として0を設定します。
from collections import defaultdict
a = defaultdict(int,{'a': 1, 'b': 2})
print(a) # 出力: defaultdict(<class 'int'>, {'a': 1, 'b': 2})
print(a['a']) # 出力: 1
print(a['b']) # 出力: 2
# 存在しないキー
print(a['c']) # 出力: 0
# アクセスしたためa['c']が自動で設定されている
print(a) # 出力: defaultdict(<class 'int'>, {'a': 1, 'b': 2, 'c': 0})
これを利用することで、アクセスの頻度分析などが簡単にできます。
from collections import defaultdict
accesses = '''
example.com
example.net
example.org
example.com
example.net
example.com
'''
access_list = accesses.strip().split('\n')
access_frequency = defaultdict(int)
for access in access_list:
# access_frequency[access]がまだない時は0+1、ある場合は+1をするという処理
access_frequency[access]+=1
print(access_frequency)
# 出力: defaultdict(<class 'int'>, {'example.com': 3, 'example.net': 2, 'example.org': 1})
defaultdictのネスト構造
先ほど挙げたアクセス分析のキーを2つにしたものを考えます。
access_frequency[url]から、access_frequency[date][url]になるイメージです。
from collections import defaultdict
accesses = '''
12-01,example.com
12-01,example.net
12-02,example.org
12-02,example.com
12-03,example.net
12-03,example.com
'''
access_list = accesses.strip().split('\n')
access_frequency = defaultdict(lambda: defaultdict(int))
for access in access_list:
date, url = access.split(',')
access_frequency[date][url]+=1
print(access_frequency)
# 出力: {'12-01': defaultdict(<class 'int'>,
# {'example.com': 1,
# 'example.net': 1}),
# '12-02': defaultdict(<class 'int'>,
# {'example.com': 1,
# 'example.org': 1}),
# '12-03': defaultdict(<class 'int'>,
# {'example.com': 1,
# 'example.net': 1})})
日付ごとのアクセスURLを集計できていることがわかります。
defaultdictのネスト構造について、参考にしていただけると幸いです。