collections

【Python】defaultdictで規定値を持つ辞書を定義する(collections.defaultdict)

【Python】defaultdictで規定値を持つ辞書を定義する(collections.defaultdict)

Pythonで規定値を持つ辞書を定義するcollectionsモジュールのdefaultdictについて解説します。

defaultdict(collectionsモジュール)

Python標準の辞書(dict)では、辞書にないキーの値を操作しようとするとエラーになるため、初期化を意識する必要があります。collectionsモジュールのdefaultdictを使用すると規定値を持つ辞書を扱うことができるので便利です。

defaultdictの必要性を説明するためにリスト要素の出現回数をカウントする例を最初に紹介しますが、とりあえずdefaultdictの使用方法をすぐ確認したい方はこちらからご確認ください。

リスト要素の出現回数カウント

Python標準の辞書(dict)を用いてリストに含まれている値の出現数をカウントするプログラムを考えてみます。リストに含まれる値を辞書のkeyとして、カウントを1ずつインクリメントしていきます。

例えば、以下のようにプログラムを書いたとします。

data = ["A", "B", "A", "C", "D", "B", "A", "B", "D"]
dict_count = {}

for key in data:
    dict_count[key] += 1

print(dict_count)
【実行結果例】
Traceback (most recent call last):
...(省略)...
KeyError: 'A'

上記プログラムで、”A”は最初のタイミングでは辞書内にないためにKeyErrorが発生します。解決方法として、以下のように初期化として値1を入れるように設定する方法を考えることができます。

data = ["A", "B", "A", "C", "D", "B", "A", "B", "D"]
dict_count = {}

for key in data:
    if key in dict_count:
        dict_count[key] += 1
    else:
        dict_count[key] = 1

print(dict_count)
【実行結果】
{'A': 3, 'B': 3, 'C': 1, 'D': 2}

このように対応することができるのですが、if…else…で少しコードが長くなってしまいます。辞書にないキーの場合は、デフォルトで値に0が入るようになってくれれば、回数をインクリメントする「dict_count[key] += 1」のコードのみでよくなります。

このようなときに簡単に対応できるように規定値を持つ辞書を定義できるのがcollectionsモジュールのdefaultdictです。

collections.defaultdictの定義と使い方

上記で紹介したリスト要素の出現回数をカウントするプログラムを例に用いて、collectionsモジュールのdefaultdictを使い方を紹介します。

intの0で初期化する場合

キーが存在しない場合に値を0で規定値として初期化したい場合には、以下のようにdefaultdictの引数に「int」クラスを指定して使用します。

import collections

data = ["A", "B", "A", "C", "D", "B", "A", "B", "D"]
dict_count = collections.defaultdict(int)

for key in data:
    dict_count[key] += 1

print(dict_count)
【実行結果】
defaultdict(<class 'int'>, {'A': 3, 'B': 3, 'C': 1, 'D': 2})

defaultdictを使用するためにはcollectionsモジュールをimportする必要があります。

上記を見るとデフォルトで0が設定されるため、リスト要素の出現回数をカウントできていることが分かります。

lambda関数での表現

上記例では、intクラスを指定しましたがlamda関数を使用して以下のように記載しても結果は同じになります。lambda関数に関しては「ラムダ(lambda)関数:無名関数の使い方」でまとめていますので興味があれば参考にしてください。

import collections

data = ["A", "B", "A", "C", "D", "B", "A", "B", "D"]
# lambda関数で記載
dict_count = collections.defaultdict(lambda: int())

for key in data:
    dict_count[key] += 1

print(dict_count)
【実行結果】
defaultdict(<function <lambda> at 0x0000028AC65D6310>, {'A': 3, 'B': 3, 'C': 1, 'D': 2})

上記は、int()を返却値とするlambda関数自体をdefaultdictに渡しています。「int()」はprintしてみると分かりますが0になるので、0で初期化されることになります。結果としては同じですが、defaultdictをprintしてみると先ほどは<class ‘int’>となっていたものが、function<lambda>となっている点が少し異なります。

floatの0.0で初期化する場合

floatの0.0で規定値を設定する場合もintの時と同様ですが見てみましょう。

import collections

data = ["A", "B", "A", "C", "D", "B", "A", "B", "D"]
dict_count = collections.defaultdict(float)

for key in data:
    dict_count[key] += 1.0

print(dict_count)
【実行結果】
defaultdict(<class 'float'>, {'A': 3.0, 'B': 3.0, 'C': 1.0, 'D': 2.0})

上記を見るとデフォルトで0.0が設定されて、1.0ずつインクリメントできていることが分かります。

lambda関数での表現

上記例では、floatクラスを指定しましたがlamda関数を使用して以下のように記載しても結果は同じになります。lambda関数に関しては「ラムダ(lambda)関数:無名関数の使い方」でまとめていますので興味があれば参考にしてください。

import collections

data = ["A", "B", "A", "C", "D", "B", "A", "B", "D"]
# lambdaで記載
dict_count = collections.defaultdict(lambda: float())

for key in data:
    dict_count[key] += 1.0

print(dict_count)
【実行結果】
defaultdict(<function <lambda> at 0x000001AA0507A050>, {'A': 3.0, 'B': 3.0, 'C': 1.0, 'D': 2.0})

上記は、float()を返却値とするlambda関数自体をdefaultdictに渡しています。「float()」はprintしてみると分かりますが0.0になるので、0.0で初期化されることになります。結果としては同じですが、defaultdictをprintしてみると先ほどは<class ‘float’>となっていたものが、function<lambda>となっている点が少し異なります。

list等でも同様に対応可能

上記では例として0で規定値を設定するintと、0.0で規定値を設定するfloatの例を紹介しましたが、以下のような他のクラスも同様に使用可能です。

クラス規定値
int0
float0.0
list[]
dict{}
boolFalse

関数で任意の規定値を定義可能

lambda関数のことが理解できていればintやfloatの例が「dict_count = collections.defaultdict(lambda: 0)」や「dict_count = collections.defaultdict(lambda: 0.0)」としても同様となることが分かるかと思います。

つまり、defaultdictの引数には、引数を持たない規定値が返却値となる関数を指定することができます。もし、複雑な関数が必要な場合はlambda関数ではなく、通常のdefで定義した上で引数に渡すことも可能です。

例えば、以下のようにdefで定義した関数を渡すことも可能です。

import collections


def init_dict():
    # 本来複雑な処理を書けるが例として1を返却するのみ
    return 1


data = ["A", "B", "A", "C", "D", "B", "A", "B", "D"]
dict_count = collections.defaultdict(init_dict)

for key in data:
    dict_count[key] += 1

print(dict_count)
【実行結果】
defaultdict(<function init_dict at 0x000001294089A170>, {'A': 4, 'B': 4, 'C': 2, 'D': 3})

この時、関数を渡すので()をつけずに「init_dict」を渡すことに注意しましょう。なお、上記は例として非常に簡単な1を返す関数にしましたが、より複雑な条件の初期化処理を書いてももちろん構いません。

defaultdictはdictのサブクラス

上記で紹介してきたdefaultdictは、dictのサブクラスとなっているため、dictと同じように各種メソッドを使って扱うことができます。

まとめ

Pythonで規定値を持つ辞書を定義するcollectionsモジュールのdefaultdictについて解説しました。

リストの要素数カウントの例を使いながらdefaultdictの使い方を説明しています。defaultdictは、dictのサブクラスとなっているためdictと同様に各種メソッドが使用できます。

また、defaultdictの引数には、引数を持たない規定値が返却値となる関数を指定することができますので、状況によりうまく使いこなしてもらえるとよいかと思います。

Note

defaultdictの公式ドキュメントの記載はこちらを参照してください。