collections.defaultdict()
| 対応: | Python 3.0(2008) |
|---|
『defaultdict』はキーが存在しない場合に自動でデフォルト値を生成する辞書のサブクラスです。『KeyError』を気にせずにデータを集計・グルーピングするコードが書けます。
構文
from collections import defaultdict # デフォルト値の型を指定して生成する d = defaultdict(list) # 存在しないキーに空のリストを生成する d = defaultdict(int) # 存在しないキーに 0 を生成する d = defaultdict(set) # 存在しないキーに空のセットを生成する d = defaultdict(str) # 存在しないキーに空文字列を生成する d = defaultdict(lambda: デフォルト値) # 任意のデフォルト値を指定する # 存在しないキーにアクセスすると自動でデフォルト値が生成される d['新しいキー'].append(値) # KeyError が発生しない
コンストラクタ引数と属性一覧
| 引数 / 属性 | 概要 |
|---|---|
| defaultdict(list) | 存在しないキーへのアクセス時に空のリスト『[]』をデフォルト値として生成します。グルーピング処理に使います。 |
| defaultdict(int) | 存在しないキーへのアクセス時に整数の『0』をデフォルト値として生成します。カウント処理に使います。 |
| defaultdict(set) | 存在しないキーへのアクセス時に空のセット『set()』をデフォルト値として生成します。重複を除いた集合の管理に使います。 |
| defaultdict(lambda: 値) | ラムダ式で任意のデフォルト値を指定します。0以外の数値や文字列をデフォルトにしたい場合に使います。 |
| d.default_factory | デフォルト値を生成する関数(コンストラクタ引数)を保持する属性です。 |
サンプルコード
collections_defaultdict.py
from collections import defaultdict
# defaultdict(list) でグルーピングする
students = [('夜神月', '刑事'), ('弥海砂', '法学'), ('夜神月', '格闘'), ('弥海砂', '刑事')]
by_student = defaultdict(list)
for name, subject in students:
by_student[name].append(subject) # 存在しないキーでも KeyError なし
print(dict(by_student))
# {'夜神月': ['刑事', '格闘'], '弥海砂': ['法学', '刑事']}
# 通常の辞書では事前の初期化が必要(比較)
by_student_normal = {}
for name, subject in students:
if name not in by_student_normal:
by_student_normal[name] = []
by_student_normal[name].append(subject)
# defaultdict(int) でカウントする
names = ['Light', 'Misa', 'Light', 'Soichiro', 'Misa', 'Light']
name_count = defaultdict(int)
for name in names:
name_count[name] += 1 # 存在しないキーは 0 から始まる
print(dict(name_count))
# {'Light': 3, 'Misa': 2, 'Soichiro': 1}
# defaultdict(set) で重複なしのグルーピング
visits = [('夜神月', 'Tokyo'), ('弥海砂', 'Osaka'), ('夜神月', 'Tokyo'), ('夜神月', 'Kyoto')]
visited = defaultdict(set)
for name, city in visits:
visited[name].add(city) # 重複は自動で除外される
print(dict(visited))
# {'夜神月': {'Tokyo', 'Kyoto'}, '弥海砂': {'Osaka'}}
# lambda で任意のデフォルト値を設定する
scores = defaultdict(lambda: 9999) # 未登録キャラのスコアは 9999
scores['夜神月'] = 120
print(scores['夜神月']) # 120
print(scores['L']) # 未登録のため 9999
# default_factory を確認する
d = defaultdict(list)
print(d.default_factory) # <class 'list'>
実行すると次のように出力されます。
python3 collections_defaultdict.py
{'夜神月': ['刑事', '格闘'], '弥海砂': ['法学', '刑事']}
{'Light': 3, 'Misa': 2, 'Soichiro': 1}
{'夜神月': {'Kyoto', 'Tokyo'}, '弥海砂': {'Osaka'}}
120
9999
<class 'list'>
よくあるミス
よくあるミス1: 意図せずキーが追加される
『defaultdict』は存在しないキーにアクセスするとデフォルト値でキーを追加します。「確認のためにアクセスした」だけでもキーが追加されるため注意が必要です。
from collections import defaultdict
d = defaultdict(list)
print('夜神月' in d) # False
_ = d['夜神月'] # アクセスしただけでキーが追加される
print('夜神月' in d) # True(空リストが追加されている)
print(d) # defaultdict(<class 'list'>, {'夜神月': []})
# キーの存在確認だけしたい場合はinを使う
if '弥海砂' in d:
print(d['弥海砂'])
よくあるミス2: default_factoryにインスタンスを渡す
デフォルト値の生成に同じオブジェクト(リストのインスタンスなど)を使うと、全キーで同じオブジェクトが共有されてしまいます。型(クラス)またはラムダ式を渡します。
まず、同じリストを返すラムダを使った場合の例です(全キーで共有されてしまう)。
shared_list = []
d = defaultdict(lambda: shared_list)
d['夜神月'].append('メモ帳')
print(d['L']) # ['メモ帳'](共有されている)
型を直接渡すと、キーごとに新しいオブジェクトが生成されます。
d = defaultdict(list)
d['夜神月'].append('メモ帳')
print(d['L']) # [](独立している)
概要
『defaultdict』は通常の辞書と同様に使えますが、存在しないキーにアクセスした際に『KeyError』を発生させず、自動でデフォルト値を生成してそのキーに設定する点が異なります。これにより「キーが存在するか確認してから処理する」というボイラープレートコードが不要になります。
『defaultdict(int)』によるカウント処理は『Counter』と同様のことができますが、集計以外の複雑な処理を組み合わせる場合は『defaultdict』の方が柔軟です。単純なカウントであれば『Counter』の方がコードが簡潔になります。
『defaultdict』でデフォルト値が生成されると、そのキーが辞書に追加されます。単にデフォルト値を返すだけでキーを追加したくない場合は、通常の辞書の『get()』メソッドや『setdefault()』を使う方法があります。
要素のカウントは『collections.Counter()』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。