🐍

PythonでオプションをJSONファイルとコマンドライン引数の双方から取得する

2020/11/23に公開

別記事、RubyでオプションをJSONファイルとコマンドライン引数の双方から取得する の Python 版です。

JSONと、コマンドラインオプションの双方から設定を読み込むためのクラスを作成してみます。

基本的なコンセプト

  1. オプションは一つのオブジェクトに集める。
    例: class Options
  2. オブジェクト生成時に、JSONファイルからオプションを読み込む。
    例: opt = Options('some.json') とするとJSONファイルからオプションを読み込む。
  3. オプションにアクセスするときはオブジェクトの attribute を使う。
    例: opt.option_1 のような感じでアクセスできる。
  4. コマンドライン引数は Argparse を使って一次処理を行う。
  5. オプションのオブジェクトは、Arguparse の生成した結果から値を取り込むメソッドを持つ。
    例: opt.import_opt(options) のような感じでインポートする。

実装例

https://gist.github.com/aikige/470f4ef93753638cc3a18d62e195eb19

#!/usr/bin/env python

import json
import os

TEMPLATE_SAMPLE = { 'option_1': 'bool', 'option_2': 'str', 'option_3': 'int' }

class Options:
    def __init__(self,
            filename='config.json',
            template = TEMPLATE_SAMPLE):
        self.template = template
        if (os.path.exists(filename)):
            with open(filename) as f:
                self.__import_dict(json.load(f))

    def __import_attr(self, key, value):
        if (value is None):
            return
        if (isinstance(value, eval(self.template[key]))):
            setattr(self, key, value)
        else:
            raise ValueError("invalid type")

    def __import_dict(self, d):
        for key in self.template.keys():
            self.__import_attr(key, d.get(key))

    def import_opt(self, o):
        for key in self.template.keys():
            if (hasattr(o, key)):
                self.__import_attr(key, getattr(o, key))

if __name__ == '__main__':
    import argparse

    opt = Options()
    print(vars(opt))

    parser = argparse.ArgumentParser()
    parser.add_argument('-1', '--option_1', type=bool) 
    parser.add_argument('-2', '--option_2', type=str)
    parser.add_argument('-3', '--option_3', type=int)
    args = parser.parse_args()
    print(vars(args))

    opt.import_opt(args)
    print(vars(opt))

補足

  • 定数 OPTS がオプションのデータ取り込みのためのテンプレートになっていて、名前と型の組を格納している。

Ruby版 との主な違い

  1. Ruby と違って Python には bool 型があるので、型判定を比較的簡単に組み込める(__import_attr)。
  2. Python の場合、オプション解析に Arrgparse を使うので、JSON解析と引数解析のメソッドが統一できておらず、若干冗長になる。
  3. Python の場合、attribute を読み取り専用にするのが少し難しそうなので、省略している。

Discussion