0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python の defaultdict は参照するだけで副作用が発生するので注意

Posted at

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'] の値を設定しています。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?