Ryota Kondo

Ryota Kondo

2023/09/09

Python|JSON、TOML形式のコンフィグファイルをコーディングしやすく読み込む方法

Python標準ライブラリを使ってJSONやTOML(Python3.11~)形式のファイルを読み込んだ場合は、dict型の値で読み込まれます。設定内容を取得する際はdict型ですので下の様にコーディングします。

value1 = config_dict["key1"]
# もしくは
value1 = config_dict.get("key1")

コンフィグの設定値を取得する箇所は多くなりがちですので、今回は少しでも書く量を減らしてコーディングしやすいようにする方法について説明します。下の様にコンフィグ.キー名で設定値を取得できるようになります。

value1 = config_dict.key1
スポンサーリンク

概要

やり方はコンフィグを読み込むデータ型をdictから、dictを継承したオリジナルクラスのデータ型に変更します。オリジナルクラスのコードはとても簡単で下の通りです。

class ConfigDict(dict):

    __getattr__ = dict.get

何をしているのか説明します。__getattr__は存在しない属性を呼び出した場合に実行されるObjectの特殊メソッドで、上の様にすると値を取得するdict.getの処理を実行するようになります。つまり属性でdictのキーを指定し、値が取得できるようになります。

例えばconfig_dict.get("key1") config_dict.key1 で同じ値が取得可能です。
補足として、存在しないコンフィグキーにアクセスした場合は例外は発生せず、Noneが返却されます。また、getcopyなどdictに存在する属性をコンフィグのキーにすることはできません。

JSONの場合のコード

JSON形式のコンフィグファイルを読み込むコードは下の通りです。json.load関数のobject_hookオプションにConfigDictを指定することで、dict型の代わりにConfigDict型でファイルを読み込むようにしています。

config.py
import json

class ConfigDict(dict):
    """
    __getattr__ は存在しない属性を呼び出した場合に実行されるObjectの
    特殊メソッドで、下の様にするとdict.getの処理を実行する。
    これにより属性でdictのキーを指定でき、値が取得できるようになる。
    例 config_dict.get("key1") と config_dict.key1 で同じ値が取得可能。
    """

    __getattr__ = dict.get

def get_json_config(config_path):
    """JSON形式のコンフィグファイルを読込み、ConfigDict型で返す"""
    with open(config_path) as f:
        return json.load(f, object_hook=ConfigDict)
スポンサーリンク

TOMLの場合のコード

TOML形式のコンフィグファイルを読み込むコードは下の通りです。JSONのload関数と異なりdict型の代わりを設定するオプションはないため、一度dict型で読み込みConfigDict型に変換するようにしています。変換する際は多層のコンフィグ設定でも対応できるよう、再帰的に変換処理を行うようにしています。

config.py
import tomllib

class ConfigDict(dict):
    """
    __getattr__ は存在しない属性を呼び出した場合に実行されるObjectの
    特殊メソッドで、下の様にするとdict.getの処理を実行する。
    これにより属性でdictのキーを指定でき、値が取得できるようになる。
    例 config_dict.get("key1") と config_dict.key1 で同じ値が取得可能。
    """

    __getattr__ = dict.get


def _dict_to_config_dict(target):
    """targetを再帰的に探索し、dict型であればConfigDict型に変換する"""

    if isinstance(target, dict):
        for key, value in target.items():
            target[key] = _dict_to_config_dict(value)

    if isinstance(target, list):
        for index, item in enumerate(target):
            target[index] = _dict_to_config_dict(item)

    if isinstance(target, dict):
        target = ConfigDict(target)

    return target

def get_toml_config(config_path):
    """TOML形式のコンフィグファイルを読込み、ConfigDict型で返す"""
    with open(config_path, mode="rb") as f:
        return _dict_to_config_dict(tomllib.load(f))
よろしければ、SNSでシェアをお願いします!
関連するタグの記事はこちらから
スポンサーリンク
Ryota Kondo
Ryota Kondo

システムエンジニア・プログラマー|このブログサイトの運営もしています|思いついたことをまとめて記事を書いています💡最近はmicroCMS、draw.io関係が多め|Twitterのフォローはお気軽に