問題提起
この記事は以下のシナリオを想定しています。
- 設定ファイルとしてYAMLを使うぞ
- YAMLはコメントを残せるんだから積極的に残していきたいよね
- プログラム上で更新したデータを設定ファイルに残したいよね
- あれ、元あったコメントを残しつつデータ記述箇所だけ更新するにはどうしたらいいんだろう...?
# ======================================================
# コメント部分
# 設定項目の説明とかあると便利
# 設定値を更新しても残しておきたい
# ======================================================
data:
default_attr_1: # 最初からある値
default_attr_2: # 最初からある値
new_attr : # 更新するときに追加される値
つまりは「コメント+dataの2属性を読みこみ」、「上書きするときにコメント+3属性で保存」したいということです。
答え
ruamel.yamlを使います。
pipで以下のようにインストールします。
pip install ruamel.yaml
そしてサンプルコードは以下です。ポイントも一緒に書いておきます。
from ruamel.yaml import YAML
yaml = YAML()
# ポイント1:コメント情報を保持するよう設定
yaml.preserve_quotes = True
yaml.indent(mapping=2, sequence=4, offset=2)
def load_config(file_path: str)->dict:
with open(file_path) as f:
config = yaml.load(f)
# ポイント2:コメント情報を保持
yaml.config = config
return config
def overwrite_config(new_config: dict, file_path: str)->None:
# ポイント3:コメント情報を保持して置いたconfigの`data`だけ上書き
yaml.config['data'] = config['data']
with open(file_path, 'w') as f:
yaml.dump(yaml.config, f)
注意すべき点は、コメント情報はYAMLオブジェクトではなくconfigオブジェクトに内包されているという点です。
そのため読み込んだ時のconfigオブジェクトをどうにかして書き込むときに参照できるようにしましょう。
YAMLオブジェクト自体にconfig属性はないので、サンプルは少々乱暴です。
最も素直かつシンプルなのは書き込むときにもload_config()を呼ぶ方法だと思います(が、筆者はそれを嫌がりました。「書き出すのにもっかい読むの?えー?」などと供述しており)。
サンプルコードに限り地味に大事な点がもう一つあります。それはYAMLオブジェクトをload_config()とoverwrite_config()の両方から参照できるように定義することです。
最後に
これがどこで役に立つのか?
- 例えば定期的に実行されるタスクにおいて、次回の実行タイミングを記録しておくようにしておけば対象がシステムが停止し、しばらくしたのち起動した場合も問題なく実行されるでしょう(期待)。
- 深層学習モデルを組み込んだシステムで、学習用システムと推論用システムが独立していた場合、この方法で最適なモデルをこの方法で設定ファイルに書き込んでおけば推論側は自動で使用するモデルを切り替えられるのでは(推測)?
- なにより人にやさしい(願望)。
たぶんもっと他にいい方法があるんだろうとは思います(し、ちゃんと設計すればこれが必要になる場面がないかもしれません)が...学習の証跡の1つとして残しておきます。