collections

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

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

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

defaultdictcollections モジュール)

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 を使い方を紹介します。

int0 で初期化する場合

キーが存在しない場合に値を 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 で初期化されることになります。結果としては同じですが、defaultdictprint してみると先ほどは <class 'int'> となっていたものが function<lambda> となっている点が少し異なります。

float0.0 で初期化する場合

float0.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 で初期化されることになります。結果としては同じですが、defaultdictprint してみると先ほどは <class 'float'> となっていたものが、function<lambda> となっている点が少し異なります。

list 等でも同様に対応可能

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

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

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

lambda 関数のことが理解できていれば intfloat の例が「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 を返す関数にしましたが、より複雑な条件の初期化処理を書いてももちろん構いません。

defaultdictdict のサブクラス

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

まとめ

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

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

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

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

ソースコード

上記で紹介しているソースコードについては GitHub にて公開しています。参考にしていただければと思います。

あわせて読みたい
【Python Tech】プログラミングガイド
【Python Tech】プログラミングガイド

ABOUT ME
ホッシー
ホッシー
システムエンジニア
はじめまして。当サイトをご覧いただきありがとうございます。 私は製造業のメーカーで、DX推進や業務システムの設計・開発・導入を担当しているシステムエンジニアです。これまでに転職も経験しており、以前は大手電機メーカーでシステム開発に携わっていました。

プログラミング言語はこれまでC、C++、JAVA等を扱ってきましたが、最近では特に機械学習等の分析でも注目されているPythonについてとても興味をもって取り組んでいます。これまでの経験をもとに、Pythonに興味を持つ方のお役に立てるような情報を発信していきたいと思います。どうぞよろしくお願いいたします。

※キャラクターデザイン:ゼイルン様
記事URLをコピーしました