プログラムを書いていると、あとから変えたくなる値が少しずつ増えていきます。接続するサーバーの名前や、ログを保存するフォルダの場所などがその代表です。
最初はコードの中に直接書いてしまいがちですよね。でも、値を変えるたびにコードを開いて書き換えるのは、思った以上に面倒でミスも起きやすい作業です。
そんなときに役立つのが、設定を別のファイルに切り出すという考え方です。Pythonには、INIという形式の設定ファイルを扱うためのconfigparserが標準で用意されています。
今回は、configparserの基本的な使い方を、手元のPython 3.11で動かした結果とともに紹介します。初めて設定ファイルを扱う方でも読み進められるように、ひとつずつ説明していきますね。
configparserとは何か¶
最初に、configparserがどんな道具なのかを整理しておきましょう。configparserは、INI形式のテキストファイルを読み込んだり、書き出したりするための標準ライブラリです。
標準ライブラリなので、pipでのインストールは不要です。Pythonが入っていれば、import configparser と書くだけですぐに使えます。
INIファイルは、Windowsのアプリなどで昔から使われてきた、とても素朴な設定ファイルの形式です。拡張子は.iniが一般的ですが、.cfgや.confといった名前で見かけることもあります。
書き方がシンプルなので、プログラマーではない人でも中身を読んで値を書き換えられます。この手軽さが、いまでも使われ続けている理由のひとつです。
INIファイルの書き方を見てみよう¶
configparserを使う前に、INIファイルがどんな見た目なのかを確認しておきます。今回は、次のような内容をsettings.iniという名前で保存しました。
[DEFAULT]
debug = no
timeout = 30
[database]
host = localhost
port = 5432
name = myapp
[paths]
base_dir = /home/user/app
log_dir = %(base_dir)s/logs
角カッコで囲まれた行は、セクションと呼ばれる見出しです。その下に、キーと値をイコールでつないだ行を並べていきます。
INIファイルを構成する要素を、表で整理しておきます。
| 要素 | 書き方の例 | 役割 |
|---|---|---|
| セクション | [database] | 設定をグループに分ける見出し |
| キーと値 | host = localhost | 1つの設定項目 |
| コメント | # や ; で始まる行 | メモ書き。読み込み時は無視される |
| DEFAULTセクション | [DEFAULT] | すべてのセクションに共通する値 |
イコールの代わりにコロンを使って host: localhost と書くこともできます。ただ、1つのファイルの中ではどちらかに揃えておくと読みやすくなります。
設定ファイルを読み込む基本の流れ¶
それでは、さっそくPythonから読み込んでみましょう。基本の流れは、ConfigParserのオブジェクトを作り、readメソッドでファイルを読むだけです。
次のコードで、先ほどのsettings.iniを読み込んでみました。
import configparser
config = configparser.ConfigParser()
config.read("settings.ini", encoding="utf-8")
print(config.sections())
print(config["database"]["host"])
print(config["database"]["port"])
実行すると、次のように表示されました。
['database', 'paths']
localhost
5432
config["database"]["host"] のように、辞書と同じ感覚で値を取り出せるのがわかります。辞書の扱いに慣れている方なら、すぐになじめるはずです。辞書の基本を復習したい方は、こちらの記事もどうぞ。【関連記事】Pythonの辞書(dict)とは?キーと値でデータを管理する基本を初心者向けに解説
sectionsの結果にDEFAULTが含まれていない点にも注目してください。DEFAULTは特別扱いのセクションなので、一覧には出てこない仕組みになっています。
encodingは必ず指定しておく¶
readを呼ぶときは、encoding="utf-8" を付けておくことをおすすめします。指定しないと、パソコンの環境によって決まる文字コードで読み込まれるからです。
日本語のコメントや値を書いたファイルが、Windowsでだけ文字化けしたり、エラーになったりするのはよくある話です。文字コードの仕組みが気になる方は、こちらで詳しく解説しています。【関連記事】Pythonの文字コードとは?文字化けとUnicodeDecodeErrorの直し方を初心者向けに解説
ファイルが無くてもエラーにならない¶
readには、少し意外な性質があります。指定したファイルが見つからなくても、エラーを出さずに静かに終わってしまうのです。
実際に、存在しないファイル名を渡してみると、戻り値は空のリストになりました。readは、読み込めたファイル名のリストを返してくれます。
read_files = config.read("nothing.ini", encoding="utf-8")
print(read_files) # []
この性質を知らないと、設定がまったく読まれていないのに気づかず、あとで別のエラーに悩まされます。設定ファイルが必須なら、戻り値が空かどうかを確かめて、空ならその場で止めるようにしておきましょう。
値はすべて文字列で返ってくる¶
configparserを使ううえで、いちばん大切なポイントがここです。ファイルから読み込んだ値は、数字に見えても文字列として返ってきます。
先ほどのportを repr で表示してみると、'5432' とクォートつきで出てきました。このまま足し算をすると、計算ではなく文字列の連結になってしまいます。
そこで用意されているのが、型を変換しながら取り出すメソッドです。よく使うものを表にまとめました。
| メソッド | 返す型 | 使いどころ |
|---|---|---|
| get() | str | 文字列のまま使う値 |
| getint() | int | ポート番号や件数 |
| getfloat() | float | 割合や小数のしきい値 |
| getboolean() | bool | 機能のオンとオフ |
getintを使えば、文字列ではなく整数として受け取れます。
db = config["database"]
port = db.getint("port")
print(port, type(port)) # 5432 <class 'int'>
数字ではない値をgetintで読もうとすると、ValueErrorになります。たとえばnameの値であるmyappを読ませると、intに変換できないというエラーが出ました。
getbooleanが受け付ける書き方¶
真偽値を読むgetbooleanは、なかなか気が利いています。trueとfalseだけでなく、人が書きやすい表現もまとめて受け付けてくれるのです。
受け付ける値を、手元で確認した結果をもとに整理しました。大文字と小文字は区別されません。
| Trueになる値 | Falseになる値 |
|---|---|
| 1 | 0 |
| yes | no |
| true | false |
| on | off |
これ以外の値、たとえばenableなどを書くとValueErrorになります。設定ファイルを人に渡すときは、どの書き方が使えるのかをコメントで添えておくと親切です。
値が無いときの既定値とDEFAULTセクション¶
設定ファイルに書かれていないキーを読もうとすると、KeyErrorが発生します。すべての項目を必ず書いてもらうのは、現実的ではありませんよね。
そんなときは、fallbackという引数で既定値を渡しておきます。キーが見つからなければ、その値が代わりに返ってくる仕組みです。
user = config.get("database", "user", fallback="admin")
print(user) # admin
retry = config.getint("database", "retry", fallback=3)
print(retry) # 3
getintやgetbooleanでもfallbackは使えます。キーが省略されてもプログラムが止まらないので、設定ファイルを最小限の内容にできます。
もうひとつの方法が、冒頭で書いたDEFAULTセクションです。ここに書いた値は、すべてのセクションから参照できるようになります。
実際に、databaseセクションにはtimeoutを書いていませんが、config["database"]["timeout"] は30を返しました。同じく、debugをgetbooleanで読むとFalseになります。
全体で共通の値はDEFAULTにまとめ、セクションごとに違う値だけを個別に書く。こうしておくと、同じ設定を何度も書かずに済みます。
知っておきたい3つの落とし穴¶
configparserは手軽な反面、知らないと戸惑う動きがいくつかあります。私が実際に手元で試して、つまずきやすいと感じたものを3つ紹介します。
キーの大文字と小文字は区別されない¶
1つ目は、キー名の大文字と小文字です。configparserは、キー名を読み込むときに自動で小文字へ変換しています。
試しに MyKey = 1 と書いたファイルを読むと、キーの一覧には小文字のmykeyだけが入っていました。読むときも区別されないので、config["a"]["MYKEY"] でも値が取れます。
公式ドキュメントによると、この変換はoptionxformというメソッドが担当しています。区別したい場合は、config.optionxform = str と設定すれば大文字と小文字をそのまま扱えます。
%記号はそのまま書けない¶
2つ目は、パーセント記号です。ConfigParserには、%(base_dir)s のように書くと、別のキーの値を埋め込める補間という機能があります。
先ほどのファイルのlog_dirは、この仕組みで /home/user/app/logs として読み込まれました。便利な一方で、pct = 50% のように%を普通の文字として書くと、InterpolationSyntaxErrorになってしまいます。
解決策は、%を2つ重ねて 50%% と書くことです。補間を使わないなら、ConfigParser(interpolation=None) として最初から機能を切っておく方法もあります。
書き戻すとコメントが消える¶
3つ目は、書き込みに関する注意点です。configparserはwriteメソッドで設定をファイルに保存できますが、公式ドキュメントにも元のファイルのコメントは保持されないと書かれています。
人が丁寧に書いた説明コメントが、プログラムで保存した瞬間に消えてしまうわけです。人が編集するファイルと、プログラムが書き換えるファイルは、分けて管理するのが安全です。
設定ファイルを書き出す方法¶
ここまでは読み込みを中心に見てきました。続いて、プログラムから設定ファイルを作る方法も確認しておきましょう。
辞書のようにセクションと値を代入して、最後にwriteで書き出します。
import configparser
config = configparser.ConfigParser()
config["app"] = {"title": "My Tool", "max_items": "50"}
config["app"]["debug"] = "yes"
with open("output.ini", "w", encoding="utf-8") as f:
config.write(f)
ここで気をつけたいのが、値には文字列しか代入できない点です。config["app"]["max_items"] = 50 のように整数を入れると、TypeErrorになりました。数値を保存したいときは、str(50) のように変換してから渡します。
ほかの設定ファイル形式との使い分け¶
設定ファイルの形式は、INIのほかにもいくつかあります。どれを選ぶか迷ったときの目安を、表にまとめました。
| 形式 | 読むための道具 | 型の扱い | 向いている場面 |
|---|---|---|---|
| INI | configparser | すべて文字列 | 人が編集する単純な設定 |
| TOML | tomllib | 数値や真偽値を区別 | 新しく作るPythonプロジェクト |
| JSON | json | 数値や真偽値を区別 | プログラム同士のデータ受け渡し |
| 環境変数 | os.environ | すべて文字列 | パスワードやサーバーごとの値 |
これから新しく作るなら、型を区別してくれるTOMLも有力な選択肢です。読み込みはPython 3.11からtomllibで行えます。【関連記事】Pythonのtomllibとは?設定ファイルをTOMLで読み込む方法を初心者向けに解説
一方で、既存のツールや社内システムがINIを使っている場面はまだまだ多くあります。読み書きの方法を知っておいて損はありません。
パスワードやAPIキーのような秘密の情報は、設定ファイルではなく環境変数で渡すのが基本です。設定ファイルをGitで共有してしまうと、うっかり公開される危険があるからです。【関連記事】環境変数とは?PythonでAPIキーを安全に扱う.envと os.environ の基本を初心者向けに解説
現場で学んだ、設定ファイルの扱い方¶
私は10年ほどエンジニアとして開発に関わってきましたが、設定ファイルにまつわる失敗は何度も経験しています。中でも忘れられないのは、本番サーバーで設定が読まれていなかったトラブルです。
原因は、スクリプトを別のフォルダから実行したため、相対パスで指定したsettings.iniが見つからなかったことでした。readがエラーを出さないため、既定値のまま動き続け、しばらく誰も気づきませんでした。
それ以来、設定ファイルの場所はスクリプトのある場所を基準に組み立てるようにしています。そのうえで、readの戻り値が空なら、すぐにsys.exitで止める形にしています。
小さな工夫ですが、設定が読まれないまま動くという、いちばん気づきにくい失敗を防げます。皆さんも、設定ファイルを使い始めたら、読み込めたかどうかを確かめる1行を入れてみてください。
まとめ¶
configparserは、INI形式の設定ファイルを読み書きするための標準ライブラリです。インストール不要で、辞書のような感覚で値を取り出せます。
押さえておきたいのは、読み込んだ値がすべて文字列で返ってくることです。数値や真偽値は、getintやgetbooleanで変換しながら受け取りましょう。
キーが無いときはfallbackで既定値を渡し、共通の値はDEFAULTセクションにまとめると、設定ファイルがすっきりします。readがファイルの不在を教えてくれない点や、%記号、コメントの消失といった落とし穴にも気をつけてください。
コードに直接書いていた値を、まずは1つだけ設定ファイルに移してみる。そこから始めると、変更に強いプログラムの書き方が自然と身についていきます。
ここまでお読みいただきありがとうございました。