[解決!Python]tomli-wモジュールを使ってTOMLファイルにデータを書き出すには解決!Python

Pythonに標準付属のtomllibモジュールはTOMLファイルからの読み込みのみをサポートしている。そこで、tomli-wモジュールを使い、辞書形式のデータをTOML形式のデータとして書き出す方法を紹介する。

» 2023年12月19日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「解決!Python」のインデックス

連載目次

from tomli_w import dump, dumps

# TOMLファイルへの出力
d = {'foo': 'FOO', 'bar': 'BAR'}

with open('sample.toml', 'wb') as f:
    dump(d, f)

from pathlib import Path
print(Path('sample.toml').read_text())  # 内容の確認
# 出力結果:
#foo = "FOO"
#bar = "BAR"

from tomllib import load
with open('sample.toml', 'rb') as f:  # 書き出したデータを読み込んでみる
    settings = load(f)

print(settings)  # {'foo': 'FOO', 'bar': 'BAR'}
print(d == settings)  # True

# 文字列への出力
s = dumps(d)
print(s)
# 出力結果:
#foo = "FOO"
#bar = "BAR"

# 基本データ型
d = {'int': 123, 'float': 0.123, 'str': 'string', 'bool': False}

s = dumps(d)
print(s)
# 出力結果:
#int = 123
#float = 0.123
#str = "string"
#bool = false

# 日付と時刻
import datetime

# オフセット付きの日付と時刻
t_delta = datetime.timedelta(hours=9)
tzinfo = datetime.timezone(t_delta)
d0 = datetime.datetime(2023, 12, 19, 5, 0, 0, 100, tzinfo)
print(d0)  # 2023-12-19 05:00:00.000100+09:00

# ローカルの日付と時刻
d1 = datetime.datetime(2023, 12, 19, 5, 0, 0, 200)
print(d1)  # 2023-12-19 05:00:00.000200

# ローカルの日付
d2 = datetime.date(2023, 12, 19)
print(d2)  # 2023-12-19

# ローカルの時刻
t0 = datetime.time(5, 0, 0, 100)
print(t0)  # 05:00:00.000100

d = {'offset datetime': d0, 'local datetime': d1,
     'local date': d2, 'local time': t0}

s = dumps(d)
print(s)
# 出力結果:
#"offset datetime" = 2023-12-19 05:00:00.000100+09:00
#"local datetime" = 2023-12-19 05:00:00.000200
#"local date" = 2023-12-19
#"local time" = 05:00:00.000100

# リストは配列に変換される
d = {'data': ['foo', 'bar', 'baz']}
s = dumps(d)
print(s)
# 出力結果:
#data = [
#    "foo",
#    "bar",
#    "baz",
#]

# 辞書のキーの値が辞書であった場合、そのキーはテーブルに変換される
d = {'dict': {'foo': 'FOO', 'bar': 'BAR'}}
s = dumps(d)
print(s)
# 出力結果:
#[dict]
#foo = "FOO"
#bar = "BAR"

# もう少し複雑な例
d = {
    'department': {
        'deep': [
            {'name': 'isshiki', 'id': 100},
            {'name': 'kawasaki', 'id': 200}
        ],
        'windows': [
            {'name': 'shimada', 'id': 10},
            {'name': 'kobayashi', 'id': 15}
        ]
    },
}

s = dumps(d)
print(s)
# 出力結果:
#[department]
#deep = [
#    { name = "isshiki", id = 100 },
#    { name = "kawasaki", id = 200 },
#]
#windows = [
#    { name = "shimada", id = 10 },
#    { name = "kobayashi", id = 15 },
#]

# 複数行文字列
d = {'foo': 'foo\nbar\nbaz'}
s = dumps(d)
print(s)  # foo = "foo\nbar\nbaz"

s = dumps(d, multiline_strings=True)
print(s)
# 出力結果:
#foo = """
#foo
#bar
#baz"""


tomli-wモジュール

 Python 3.11で導入されたtomllibモジュールは、tomliモジュールをベースとしたもので、TOMLファイルやTOML形式のデータを含んだ文字列からの読み込みのみをサポートしている。つまり、辞書形式のデータのTOMLファイルや文字列への出力はサポートしていない(詳細はPEP 680を参照)。tomllib/tomliモジュールの代わりにTOMLファイルや文字列へ書き出すのに使えるのがtomli-wモジュールだ。

 tomli-wモジュールには次の2つの関数が用意されている(インポートする際には「tomli_w」となる点に注意)。

  • dump関数:TOMLファイルへデータを書き出す
  • dumps関数:TOML形式のデータを文字列に書き出す

 以下では、これらの関数の使い方とPythonの各種オブジェクトがどのように書き出されるかを見ていく。また、TOMLのフォーマットについては「tomllibモジュールを使ってTOMLファイルを読み込むには」で簡単に紹介しているので興味のある方はそちらを参照されたい。

TOMLファイルへの書き出し

 tomli_w.dump関数はTOMLファイルへの辞書形式のデータの書き出しに使用する。その基本的な使い方を以下に示す(「pip install tomli-w」「py -m pip install tomli-w」などのコマンドで事前にtomli-wモジュールをインストールしておく必要がある)。

from tomli_w import dump, dumps

d = {'foo': 'FOO', 'bar': 'BAR'}

with open('sample.toml', 'wb') as f:
    dump(d, f)


 この例ではシンプルな辞書を用意して、それをTOMLファイル(sample.toml)に書き出している。ファイルをオープンする際にはモードとして'wb'を指定して、バイナリファイルとしてオープンする必要がある点には注意しよう。その後は、dump関数に辞書データとファイルオブジェクトを渡すだけだ。

 書き出したファイルの内容を確認してみよう。これにはpathlib.Path.read_textメソッドを使用している。

from pathlib import Path
print(Path('sample.toml').read_text())  # 内容の確認
# 出力結果:
#foo = "FOO"
#bar = "BAR"


 辞書データが「foo = "FOO"」「bar = "BAR"」のようにTOML形式で記述されていることが分かる。以下はこれをtomllibモジュールが提供するload関数を使って読み込んでいるところだ。

from tomllib import load
with open('sample.toml', 'rb') as f:  # 書き出したデータを読み込んでみる
    settings = load(f)

print(settings)  # {'foo': 'FOO', 'bar': 'BAR'}
print(d == settings)  # True


 辞書形式のデータとして復元され、元のデータとも同じであることが確認できた。

文字列への出力

 文字列に出力するには、tomli_w.dumps関数を使用する。以下に簡単な使用例を示す。

s = dumps(d)
print(s)
# 出力結果:
#foo = "FOO"
#bar = "BAR"


 ご覧の通り、tomli_w.dumps関数に辞書データを渡すだけでよい。なお、以下ではtomli_w.dumps関数を使って文字列にTOMLを出力した結果を掲載していくことにする。

PythonのオブジェクトとTOMLのデータ型

 Pythonの各種オブジェクトとTOMLのデータ型との対応関係は次のようになっている。

Pythonのデータ型 TOMLのデータ型
文字列 文字列
整数 整数
浮動小数点数 浮動小数点数
ブール値(True/False) ブーリアン(true/false)
datetime.datetime(UTCオフセット付き) オフセット付き日時
datetime.datetime(UTCオフセットなし) ローカルの日時
datetime.date ローカルの日付
datetime.time ローカルの時刻
リスト 配列
PythonのオブジェクトとTOMLのデータ型との対応関係

 以下に文字列、整数、浮動小数点数、ブール値を値として含む辞書をTOML形式に変換する例を示す。

d = {'int': 123, 'float': 0.123, 'str': 'string', 'bool': False}

s = dumps(d)
print(s)
# 出力結果:
#int = 123
#float = 0.123
#str = "string"
#bool = false


 特筆すべきことはないが、ブール値の記述がPythonではTrue/Falseなのに対して、TOMLではtrue/falseである点には注意しよう。

日時や日付、時刻

 日付や時刻の変換例を以下に示す。日時の末尾にUTCオフセット(±HHMMSS.ffffff)があればオフセット付きの日時として、なければローカルの日時として扱われる。

import datetime

# オフセット付き日時
t_delta = datetime.timedelta(hours=9)
tzinfo = datetime.timezone(t_delta)
d0 = datetime.datetime(2023, 12, 19, 5, 0, 0, 100, tzinfo)
print(d0)  # 2023-12-19 05:00:00.000100+09:00

# ローカルの日時
d1 = datetime.datetime(2023, 12, 19, 5, 0, 0, 200)
print(d1)  # 2023-12-19 05:00:00.000200

# ローカルの日付
d2 = datetime.date(2023, 12, 19)
print(d2)  # 2023-12-19

# ローカルの時刻
t0 = datetime.time(5, 0, 0, 100)
print(t0)  # 05:00:00.000100

d = {'offset datetime': d0, 'local datetime': d1,
     'local date': d2, 'local time': t0}

s = dumps(d)
print(s)
# 出力結果:
#"offset datetime" = 2023-12-19 05:00:00.000100+09:00
#"local datetime" = 2023-12-19 05:00:00.000200
#"local date" = 2023-12-19
#"local time" = 05:00:00.000100


リスト

 PythonのリストはTOMLの配列に変換される(試したところではタプルも同様だと思われる)。以下に例を示す。

d = {'data': ['foo', 'bar', 'baz']}
s = dumps(d)
print(s)
# 出力結果:
#data = [
#    "foo",
#    "bar",
#    "baz",
#]


 Pythonの集合は配列には変換されずに、TypeError例外を発生させる。

辞書のキーの値が辞書の場合

 tomli_w.dump/dumps関数は辞書を受け取り、それをTOML形式の記述に変換するが、辞書のキーの値もまた辞書であった場合、そのキーはテーブルに変換される。以下に簡単な例を示す。

d = {'dict': {'foo': 'FOO', 'bar': 'BAR'}}
s = dumps(d)
print(s)
# 出力結果:
#[dict]
#foo = "FOO"
#bar = "BAR"


 ここでは、'dict'キーの値は辞書となっている。これをtomli_w.dumps関数に渡すと、出力結果から分かるようにテーブル名をdictとするテーブルが作られ、その値となっていた辞書のキー/値の組みがそのテーブルの下に記述される。

 もう少し複雑な辞書の例を見てみよう。

d = {
    'department': {
        'deep': [
            {'name': 'isshiki', 'id': 100},
            {'name': 'kawasaki', 'id': 200}
        ],
        'windows': [
            {'name': 'shimada', 'id': 10},
            {'name': 'kobayashi', 'id': 15}
        ]
    },
}


 この例では、トップレベルの'department'キーの値が辞書となっていて、そこには'deep'と'windows'の2つのキーが含まれている。それらの値は辞書を要素とするリストだ。これをtomli_w.dumps関数でTOMLに書き出すと次のようになる。

s = dumps(d)
print(s)
# 出力結果:
#[department]
#deep = [
#    { name = "isshiki", id = 100 },
#    { name = "kawasaki", id = 200 },
#]
#windows = [
#    { name = "shimada", id = 10 },
#    { name = "kobayashi", id = 15 },
#]


 トップレベルの'department'キーを名前とするテーブルが作られ、その値であった2つのキーがその下に置かれ、辞書を要素とするリストだったものは、インラインテーブルを要素とする配列に変換された。

 上の辞書を少し変更して、'deep'と'windows'の2つのキーの値が辞書だったとしよう。

d = {
    'department': {
        'deep': {
            'names': ['isshiki', 'kawasaki'],
            'ids': [100, 200]
        },
        'windows': {
            'names': ['shimada', 'kobayashi'],
            'ids': [10, 15]
    }
    },
}

s = dumps(d)
print(s)


 この場合は、ネストしたテーブルが生成される(実行結果は省略する。興味のある方は試してみてほしい)。

複数行文字列

 キーの値が文字列であり、その文字列に改行が含まれていた場合、tomli_wでは改行はエスケープして文字列内に含まれる。以下に例を示す。

d = {'foo': 'foo\nbar\nbaz'}
s = dumps(d)
print(s)  # foo = "foo\nbar\nbaz"


 「\n」としてエスケープ表記されているのが分かるはずだ。ただし、読みやすさなどを勘案して、エスケープ表記ではなく、トリプルクオートを用いて改行文字は改行文字として表記した方がよい場合もある。そういうときには、tomli_w.dump/dumps関数のmultiline_strings引数にTrueを指定する。以下に例を示す。

s = dumps(d, multiline_strings=True)
print(s)
# 出力結果:
#foo = """
#foo
#bar
#baz"""


 「multiline_strings=True」を指定すると、上の結果から分かるように、文字列がトリプルクオートを使って囲まれ、そこに改行文字は改行文字として埋め込まれる。TOMLファイルの可読性などでこちらの方が好ましいときもあるだろう。

「解決!Python」のインデックス

解決!Python

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。