本記事では、Pythonでのdefaultdictの使い方を解説します。defaultdictを使うと、存在しないキーにアクセスした際のエラーを防ぎ、コードをより簡潔に書くことができます。
defaultdictとは
defaultdictはPythonの標準ライブラリcollectionsモジュールに含まれるクラスで、通常の辞書型(dict)を拡張したものです。
dictとdefaultdictの最大の違いは、存在しないキーにアクセスした際の挙動です。
- dict: 存在しないキーにアクセスするとKeyErrorが発生します
- defaultdict: 存在しないキーにアクセスすると、指定したデフォルト値を返し、そのキーと値のペアを辞書に追加します
以下に具体的なコード例で比較してみましょう。
# 通常のdictの場合
normal_dict = {}
try:
print(normal_dict["存在しないキー"])
except KeyError as e:
print(f"エラーが発生: {e}")
# 出力例: エラーが発生: '存在しないキー'
# defaultdictの場合
from collections import defaultdict
default_dict = defaultdict(int) # デフォルト値として0を返す
print(default_dict["存在しないキー"]) # 出力例: 0
print(default_dict) # 出力例: defaultdict(<class 'int'>, {'存在しないキー': 0})
この挙動の違いにより、defaultdictは特に要素の集計やグループ化など、キーが動的に増えていく処理で非常に便利です。
構文
defaultdictを使用するには、まずcollectionsモジュールからインポートする必要があります。
from collections import defaultdict
基本的な構文は次のとおりです。
# defaultdict(default_factory)
my_dict = defaultdict(default_factory)
ここでdefault_factoryは、存在しないキーにアクセスした際にデフォルト値を生成する関数です。以下はよく使われるdefault_factoryの例です。
- int: 0を返す
- float: 0.0を返す
- str: 空文字列を返す
- list: 空リストを返す
- dict: 空辞書を返す
- set: 空集合を返す
- lambda関数: カスタムデフォルト値を返す
利用例
単語の出現回数をカウントする
単語の出現回数をカウントする場合、defaultdictを使うと簡潔に書けます。
from collections import defaultdict
text = "apple banana apple orange banana apple"
word_count = defaultdict(int)
for word in text.split():
word_count[word] += 1
print(word_count)
# 出力例: defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'orange': 1})
通常のdictを使った場合、キーの存在チェックが必要です。
# 通常のdictで同じ処理を行う場合(比較用)
text = "apple banana apple orange banana apple"
word_count = {}
for word in text.split():
if word in word_count:
word_count[word] += 1 # キーの存在確認が必要
else:
word_count[word] = 1 # 存在しない場合は初期化が必要
グループ化する
名前をアルファベットの頭文字でグループ化する例です。
from collections import defaultdict
names = ["Alice", "Bob", "Charlie", "David", "Alex", "Barbara"]
grouped_names = defaultdict(list)
for name in names:
grouped_names[name[0]].append(name) # 頭文字をキーにしてリストに名前を追加
print(grouped_names)
# 出力例: defaultdict(<class 'list'>, {'A': ['Alice', 'Alex'], 'B': ['Bob', 'Barbara'], 'C': ['Charlie'], 'D': ['David']})
ネストしたdefaultdictの作成
二次元以上の構造を扱う場合にも便利です。
from collections import defaultdict
# 二次元のdefaultdict
nested_dict = defaultdict(lambda: defaultdict(int))
# データの追加
nested_dict["user1"]["apple"] = 5
nested_dict["user1"]["banana"] = 2
nested_dict["user2"]["apple"] = 1
# 存在しないキーでもエラーにならない
print(nested_dict["user3"]["orange"]) # 出力例: 0
print(dict(nested_dict))
# 出力例: {'user1': defaultdict(<class 'int'>, {'apple': 5, 'banana': 2}), 'user2': defaultdict(<class 'int'>, {'apple': 1}), 'user3': defaultdict(<class 'int'>, {'orange': 0})}
独自のデフォルト値を設定する
lambdaを使って独自のデフォルト値を設定できます。
from collections import defaultdict
# "Unknown"をデフォルト値とする辞書
my_dict = defaultdict(lambda: "Unknown")
my_dict["known_key"] = "Known Value"
print(my_dict["known_key"]) # 出力例: Known Value
print(my_dict["unknown_key"]) # 出力例: Unknown
注意点
メモリ使用量に注意
defaultdictは存在しないキーにアクセスするたびに新しいエントリを作成するため、無制限にキーが増える可能性があります。特に大量のデータを扱う場合はメモリ使用量に注意しましょう。
from collections import defaultdict
# 誤った使用例
my_dict = defaultdict(list)
# typoによる意図しないキーの作成
my_dict["correct_key"].append(1)
my_dict["corect_key"].append(2) # タイプミス
print(len(my_dict)) # 出力例: 2 (意図せず2つのキーができてしまった)
デフォルト値のミュータブル性に注意
mutable(変更可能)なオブジェクトをデフォルト値にする場合、全てのキーで同じオブジェクトを参照することはありません。各キーに対して新しいオブジェクトが作成されます。
from collections import defaultdict
# 各キーに対して新しいリストが作成される
my_dict = defaultdict(list)
my_dict["key1"].append(1)
my_dict["key2"].append(2)
print(my_dict["key1"]) # 出力例: [1]
print(my_dict["key2"]) # 出力例: [2]
default_factoryにNoneを指定するとエラーになる
default_factoryにNoneを指定すると、通常のdictと同様に存在しないキーにアクセスした際にKeyErrorが発生します。
from collections import defaultdict
my_dict = defaultdict(None)
try:
print(my_dict["non_existent_key"])
except KeyError as e:
print(f"エラーが発生しました: {e}")
# 出力例: エラーが発生しました: 'non_existent_key'
まとめ
defaultdictは以下のような状況で特に有用です。
- 存在しないキーに対する処理を簡略化したい場合
- 集計処理やグループ化を行う場合
- ネストした辞書構造を扱う場合
- キーに対して初期値を自動的に設定したい場合
通常のdictと比較して、コードの可読性を高め、エラーハンドリングを減らすことができます。ただし、意図しないキーの作成によるメモリ消費に注意する必要があります。
Pythonのデータ処理において、defaultdictは非常に強力なツールであり、適切に使いこなすことでコードの品質を向上させることができます。