Python の defaultdict では初期化の処理を一部省略できます。例えば int を指定すれば、デフォルト値を 0 として計算を行ってくれます。
from collections import defaultdict
data = [
['chino', 4],
['maya', 8],
['megu', 2],
['chino', 1200],
['maya', 800],
['megu', 1100],
]
d = defaultdict(int)
for k, v in data:
d[k] += v
print(d['chino']) # => 1204
__getitem__
の副作用
ただ、defaultdict には __getitem__
を呼び出すだけでキーが追加されるという副作用があるため注意してください。例えば、以下の例ではキーが 3個から 7個に増加してしまっています。
print(len(d)) # => 3
probe = ['cocoa', 'chino', 'rize', 'chiya', 'syaro']
for k in probe:
print(f'{k} => {d[k]}') # __getitem__
print(len(d)) # => 7
副作用を発生させずに値の取得だけしたい場合は get(key)
や get(key, default)
を使用してください。
print(len(d)) # => 3
probe = ['cocoa', 'chino', 'rize', 'chiya', 'syaro']
for k in probe:
print(f'{k} => {d.get(k, "unknown")}')
print(len(d)) # => 3
敢えて副作用を発生させるメリット
ですが、敢えて副作用を発生させることで、list や dict などを初期値にすることもでき、実用上は非常に便利な仕様かなと思います。
data2 = [
['chino', '香風'],
['chino', '智乃'],
]
d = defaultdict(list)
for k, v in data2:
d[k].append(v)
# d.get(k, []).append(v) # これはダメ
print(d['chino']) # => ['香風', '智乃']
このように、返り値のリストや辞書に対してメソッドを呼び出したりできるようになります。
もし d.get(k, [])
と書いてしまった場合、新しく生成された []
が辞書 d
に結び付けられないため、その返り値 []
に対して .append(v)
を呼び出しても、その値は辞書には残らず消えてしまうことになります。
defaultdict の defaultdict
同じパターンで、defaultdict の defaultdict も作ることができます。
dd = defaultdict(lambda: defaultdict(int))
def birthday():
dd['chino']['age'] += 1
この birthday()
メソッドが初めて呼ばれると、dd['chino']
という呼び出しによって defaultdict(int)
のインスタンスが作成され、そのインスタンスに対して ['age']
の値を設定しています。