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
が返却されます。また、get
やcopy
などdictに存在する属性をコンフィグのキーにすることはできません。
JSONの場合のコード
JSON形式のコンフィグファイルを読み込むコードは下の通りです。json.load
関数のobject_hook
オプションにConfigDict
を指定することで、dict型の代わりにConfigDict型でファイルを読み込むようにしています。
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型に変換するようにしています。変換する際は多層のコンフィグ設定でも対応できるよう、再帰的に変換処理を行うようにしています。
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))
