1.基本編
1-A INI形式のコンフィグについて
PythonのconfigparserはINI形式のコンフィグのみに対応しています。
INI形式のコンフィグは、[]
でセクションをkey=value
で項目名と設定値を表した形式のコンフィグです。
以下にINIファイルのコンフィグのサンプルを示します。
ざっくりとした解説はサンプル内にコメントとして書いておいたのでそちらをご確認ください。
# でコメント行を表す。 | |
; もコメント行を表す。 | |
### 基本的な書き方 ### | |
# []でセクション、<項目名=値>の形式で各項目を表す。値は全て文字列として扱われる。 | |
[SECTION1] | |
HOGE=0123 | |
# 同じ項目名が複数存在するとエラーになる。 | |
# 大文字・小文字の区別はされないので以下のような値もエラーになるので注意。 | |
# hoge=4567 | |
### 応用的な書き方 ### | |
[section1] | |
# セクション名は大文字と小文字の区別がされるのでSECTION1とsection1は別扱いされる。 | |
# =両側のスペースはあってもなくてもOK。 | |
hoge = aaaa | |
# 'や"も文字列の一部として扱われる。 | |
piyo="bbbb" | |
# 値無しの項目を設定しても良い。 | |
no_value= | |
# マルチバイト文字も使用可(使わないほうがいいと思うけど) | |
項目名だよ=設定値だよ | |
# 項目名とか値に半角スペースも使用可(使わないほうがいいと思うけど) | |
my key=my value |
あと、↑に書き忘れましたが、Pythonのconfigparserの場合はセクション無しの項目はエラーになります。
1-B コンフィグ読み込み
1-Aで紹介したコンフィグファイルを読み込むスクリプトを書いてみます。
「コンフィグ内の項目に辞書ライクにアクセスできる」と思っておけば基本は困らないかと思います。
例によって解説は以下のサンプル内のコメントを見てください。
import configparser | |
def main(): | |
# configparserのインスタンスを生成 | |
config = configparser.ConfigParser() | |
# コンフィグファイルを読み込み | |
config.read('./sample.conf') | |
# 読み込んだコンフィグは辞書ライクに扱うことができる(でもdict型では無い) | |
print(config['SECTION1']['hoge']) | |
# get()で値を取得することもできる。取得時に色々(後述)する場合はこれ。 | |
print(config['SECTION1'].get('hoge')) | |
# セクション名のlistを取り出す | |
sections = config.sections() | |
print(sections) | |
# セクション内の項目を丸っと取り出す | |
conf_section1 = config['section1'] | |
# セクション内も辞書ライクにアクセス可能 | |
print(conf_section1['HOGE']) | |
# イテレーションもOK | |
for k, v in conf_section1.items(): | |
print("{0}={1}".format(k, v)) | |
if __name__ == '__main__': | |
main() |
このスクリプトを実行すると以下の出力が得られます。
mathkuro002:python kuro$ python3 ./configparser_sample.py 0123
0123
0123
[‘SECTION1’, ‘section1’]
aaaa
hoge=aaaa
piyo=”bbbb”
no_value=
項目名だよ=設定値だよ
my key=my value
若干戸惑うのは戻ってくるのが純粋なdictではなくconfigparserオブジェクトであるところですかね。
以降も辞書として使い続けたり、Configクラスにするとかの場合は読み込んだ時点でdict()で変換してやったりしたほうが色々考えなくていいので楽かもしれません。
2.応用編
2-A デフォルト値の設定
コンフィグのデフォルト値(コンフィグファイルに値が設定されていない場合に使用される値)を設定したい場合は、デフォルト値のdictを用意しconfigparserに読み込ませます。
デフォルト値を設定する場合は以下の点に注意する必要があります。
・デフォルト値を先にconfigparserに読ませること
※同じ項目は後勝ちで上書きされるため。
・”key=”のように空の項目をファイルに設定しないこと
※空の項目が設定されていると、””でデフォルト値が上書きされてしまう(allow_no_valueを指定している場合はNone)。
一応サンプルも置いておきます。
# 設定したいデフォルト値 | |
default_configs = {'section1': {'hoge': '0000', | |
'fuga': 9999}, | |
'section2': {'hoge': 'hoge_value'}} | |
# configparserのインスタンスを生成 | |
config = configparser.ConfigParser() | |
# デフォルト値を設定する(コンフィグファイルを読み込む前に設定しないと逆にデフォルト値で上書きされるので注意) | |
config.read_dict(default_configs) | |
# コンフィグファイルを読み込み | |
config.read('./sample.conf') |
デフォルト値を設定する方法には別の方法もあります(私はあまり好みませんが…)。
config値を取得する際にdefaultの値を設定する方法です。
argparserを使ったことがある方であればこれだけでピンとくるかもしれません。
具体的には以下の感じです。
config = configparser.ConfigParser()
config.read('./sample.conf')
conf_section1.get('key', 'value')
2-B 型・条件を指定した読み込み
前述の通り、デフォルトではコンフィグ値は全てstr型として取得されます。
ですが、取得する際にint/float/booleanを指定して取得することも可能です。
例えば、[section1]にhoge=1と設定した場合を試してみると、
config.read('./sample.conf')
print(config['section1'].getint('hoge'))
print(config['section1'].getfloat('hoge'))
print(config['section1'].getboolean('hoge'))
の出力は以下のようになります。
1
1.0
True
ただし、項目毎に設定する必要があるため少し面倒です。
本当に型を合わせる必要がある項目だけ設定する感じでしょうね。
2-C 例外
最後に例外についてチラッと触れておきます。
configparser.Errorなるものがいるのでこれをexceptで捕捉してやればいいのかと思ったのですが、そうは問屋が卸さないみたいです。例外の基底クラス仕事してよ。
全網羅はしていませんが起こりがちな異常系と例外をまとめておきます。
準正常/異常 | 発生する例外 |
---|---|
指定したコンフィグファイルが存在しない。 ファイルパスが不正。 | configparser.read()だと例外は発生しない。その後、値を取得しようとすると、KeyErrorが発生する。 configparser.read_file()だと、open()で失敗するのでそのopenの例外を補足する必要がある。 |
セクションが存在しない。 セクションに属さない項目が存在する。 | configparser.Errorが発生。 “File contains no section headers.” |
存在しないセクションを指定。 | KeyError ※辞書と同じって考えればいいけどconfigparser.Errorで拾えて欲しかった。。。 |
存在しない項目を指定。 | 同上 |
存在しない項目をget()で取得。 | 例外は発生しない。 ※Noneが返ってくる |
異なる型の値をgetint/getfloat/getbooleanで取得。 | ValueErrorが発生。 ※configparser.Errorで拾えて欲しかった。。。 |
同じ名前のセクション・項目が存在した。 | configparser.Errorが発生。 ※configparser生成時にconfigparser(strict=False)を指定すると発生しない。 |
一番初めに「pythonのconfigparserはINI形式にのみ対応しています!」とか書きましたが、よくよく考えるとjsonparser使えばjsonだって読み込めますよね。
ymlはサードパーティ製ライブラリ入れないとだめくさいですが。
まあ個人的にコンフィグはINI形式が人間様に一番読みやすいと思っているのでconfigparserがINIフレンドリーで良かったです。
え、「複雑なコンフィグはどうするんだ!!」ですか?
それは設計からやり直したほうがいいんじゃないですか。。。
参考

コメント