[解決!Python]PyYAMLモジュールを使ってYAMLデータを読み込むには:解決!Python
YAMLドキュメントをPythonオブジェクトに変換するためのPyYAMLモジュールの使い方と、YAMLドキュメントがどんな形でPythonオブジェクトに変換されるかを紹介する。
from yaml import safe_load, unsafe_load, full_load
from pathlib import Path # ファイル内容の確認用
# YAMLファイルからの読み出し
print(Path('test.yaml').read_text())
# 出力結果:
#--- # YAMLドキュメントの開始
#- foo: FOO
#- bar: BAR
#- baz: BAZ
#... # YAMLドキュメントの終了
with open('test.yaml') as f:
data = safe_load(f)
print(data) # [{'foo': 'FOO'}, {'bar': 'BAR'}, {'baz': 'BAZ'}]
# 文字列からの読み出し
s = """
- foo: FOO # これはYAMLのコメント
- bar: BAR
- baz: BAZ
"""
data = full_load(s)
print(data) # [{'foo': 'FOO'}, {'bar': 'BAR'}, {'baz': 'BAZ'}]
import yaml
from yaml import load
data = load(s, Loader=yaml.UnsafeLoader) # yaml.UnsafeLoaderとyaml.Loaderは同じ
print(data) # [{'foo': 'FOO'}, {'bar': 'BAR'}, {'baz': 'BAZ'}]
# シーケンスのパース
s = """
- foo # ブロックシーケンス
- bar
- baz
"""
data0 = safe_load(s) # load(s, Loader=yaml.SafeLoader)
print(data0) # ['foo', 'bar', 'baz']
s = """
[foo, bar, baz] # フローシーケンス
"""
data1 = full_load(s) # load(s, Loader=yaml.FullLoader)
print(data1) # ['foo', 'bar', 'baz']
print(data0 == data1) # True
# ネストしたブロックシーケンス
s = """
- L1-1
- # 次レベル
- L2-1
- L2-2
- L1-2 # レベルを元に
- # 次レベル
- # 次レベル
- L3-1
- L3-2
"""
data = safe_load(s)
print(data) # ['L1-1', ['L2-1', 'L2-2'], 'L1-2', [['L3-1', 'L3-2']]]
# ネストしたフローシーケンス
s = """
[L1-1, [L2-1, L2-2], L1-2, [[L3-1, L3-2]]]
"""
data = safe_load(s)
print(data) # ['L1-1', ['L2-1', 'L2-2'], 'L1-2', [['L3-1', 'L3-2']]]
# マッピング
s = """
foo: FOO # ブロックマッピング
bar: BAR
baz: BAZ
"""
data = safe_load(s)
print(data) # {'foo': 'FOO', 'bar': 'BAR', 'baz': 'BAZ'}
s = """
{foo: FOO, bar: BAR, baz: BAZ} # フローマッピング
"""
data = safe_load(s)
print(data) # {'foo': 'FOO', 'bar': 'BAR', 'baz': 'BAZ'}
# ネストしたブロックマッピング(マッピングを値とするマッピング)
s = """
user01:
name: isshiki
id: 100
user02:
name: kawasaki
id: 102
"""
data = safe_load(s)
print(data)
# 出力結果:
#{'user01': {'name': 'isshiki', 'id': 100}, 'user02': {'name': 'kawasaki',
# 'id': 102}}
# シーケンスを値とするマッピング
s = """
user01:
- isshiki # ブロックシーケンス
- 100
user02: [kawasaki, 102] # フローシーケンス
"""
data = safe_load(s)
print(data) # {'user01': ['isshiki', 100], 'user02': ['kawasaki', 102]}
# マッピングを要素とするシーケンス
s = """
- foo: FOO
- bar: BAZ
"""
data = safe_load(s)
print(data) # [{'foo': 'FOO'}, {'bar': 'BAZ'}]
s = """
[
foo: FOO,
{bar: BAR}
]
"""
data = safe_load(s)
print(data) # [{'foo': 'FOO'}, {'bar': 'BAR'}]
# スカラー(Unicode文字列)
s = """
forums: | # リテラルスタイル(改行はそのまま)
windows insider
deep insider
foo: > # foldedスタイル(途中の改行は空白文字に変換される)
foo and
bar
"""
data = safe_load(s)
for k, v in data.items():
print(f'{k}: {v}')
# 出力結果:
#forums: windows insider # 改行はそのまま
#deep insider
#
#foo: foo and bar # andの後の改行が半角空白文字に
s = """
single quoted: 'single
quoted scalar'
double quoted: "double
quoted scalar"
"""
data = safe_load(s)
for k, v in data.items():
print(f'{k}: {v}')
# 出力結果:
#single quoted: single quoted scalar
#double quoted: double quoted scalar
# YAMLでの明示的な型指定
s = """
int: 1 # PyYAMLが型を自動的に変換してくれる
float: 2.0
datetime: 2023-12-26T05:00:00+0900
local datetime: 2023-12-26 05:00:00
date: 2023-12-05
time: 05:00:00
str: string
sequence: [0, 1, 2]
mapping: {foo: FOO, bar: BAR}
"""
data = safe_load(s)
for k, v in data.items():
print(f'{k}: {v},\ttype: {type(v)}')
# 出力結果:
#int: 1, type: <class 'int'>
#float: 2.0, type: <class 'float'>
#datetime: 2023-12-26T05:00:00+0900, type: <class 'str'>
#local datetime: 2023-12-26 05:00:00, type: <class 'datetime.datetime'>
#date: 2023-12-05, type: <class 'datetime.date'>
#time: 05:00:00, type: <class 'str'>
#str: string, type: <class 'str'>
#sequence: [0, 1, 2], type: <class 'list'>
#mapping: {'foo': 'FOO', 'bar': 'BAR'}, type: <class 'dict'>
# YAMLタグ
s = """
none: !!null
int: !!int 20
float: !!float 10
bool: [!!bool true, !!bool false, !!bool yes, !!bool no]
list of tuple: !!omap [{foo: FOO}, {bar: BAR}]
list: !!seq [0, 1, 2]
dict: !!map {foo: FOO, bar: BAR}
"""
data = safe_load(s)
for k, v in data.items():
print(f'{k}: {v}')
# Pythonに固有のタグ
s = """
none: !!python/none
bool: [!!python/bool true, !!python/bool off]
bytes: !!python/bytes ZGVlcA==
str: !!python/str python
int: !!python/int 1
float: !!python/float 10
list: !!python/list [foo, bar, baz]
tuple: !!python/tuple [foo, bar, baz]
dict: !!python/dict {foo: FOO, bar: BAR}
"""
data = full_load(s)
for k, v in data.items():
print(f'{k}: {v}')
data = safe_load(s) # yaml.constructor.ConstructorError
data = unsafe_load(s) # OK
# 複数のYAMLドキュメントをまとめて読み出す
s = """
---
foo: FOO
...
---
bar: BAR
...
"""
from yaml import safe_load_all
contents = safe_load_all(s)
for idx, content in enumerate(contents, 1):
print(f'{idx}番目のドキュメント')
print(content)
# 出力結果:
#1番目のドキュメント
#{'foo': 'FOO'}
#2番目のドキュメント
#{'bar': 'BAR'}
print(Path('test2.yaml').read_text())
# 出力結果:
# 1つのストリーム(ファイル)に複数のYAMLドキュメント
#---
#foo: FOO # 1つ目のYAMLドキュメント
#...
#---
#bar: BAR # 2つ目のYAMLドキュメント
#...
with open('test2.yaml') as f:
contents = safe_load_all(f)
for content in contents:
print(content) # ValueError: I/O operation on closed file.
with open('test2.yaml') as f:
contents = safe_load_all(f)
result = [content for content in contents]
for content in result:
print(content)
# 出力結果:
#{'foo': 'FOO'}
#{'bar': 'BAR'}
関数 | 使われるローダー | 説明 |
---|---|---|
load | Loaderパラメーターに指定が必須 | ローダーの指定による |
safe_load | SafeLoader | YAML仕様のサブセットを読み込み可能。安全にパースできる。信頼できないソースからのYAMLデータの読み込みにはこれを使用する |
full_load | FullLoader | YAML仕様のフルセットに対応し、安全にパースができるとともに任意のコードの実行は不可能となっているが、信頼できないソースからのYAMLデータの読み込みには使わないこと |
unsafe_load | UnsafeLoader(Loader) | YAML仕様のフルセットに対応し、任意のコードの実行も可能。使用してもよいと思われるのは、信頼できるソースからのYAMLドキュメントを読み込む場合のみ |
safe_load_all | SafeLoader | 1つのストリーム(文字列や辞書など)に複数のYAMLドキュメントが含まれている場合に、それらを反復するジェネレーターを返送する。同様な関数としてfull_load_all関数、load_all関数、unsafe_load_all関数もある |
YAMLデータの読み込みに使用できる関数 |
PyYAMLモジュール
YAML(YAML Ain't Markup Language)はオブジェクトやデータをシリアライズするための言語仕様であり、可読性が高いことが特徴となっている。YAML形式で記述されたドキュメント(YAMLドキュメント)をPythonで扱うためのモジュールには幾つかあるか、ここではPyYAMLを使用する。なお、YAMLの最新仕様は1.2.2だが、2023年12月22日の時点ではPyYAMLはYAML 1.1を対象としている点には注意されたい。YAML 1.2をサポートしているruamel.yamlもあるので必要な型はそちらもチェックしよう。
以下ではPyYAMLを用いて、YAMLドキュメントをファイルまたは文字列からPythonのオブジェクトとして読み出す方法を紹介する。PyYAMLはPythonには標準で添付されていないので「pip install pyyaml」「py -m pip install pyyaml」などの方法で事前にインストールしておく必要がある。
PyYAMLでYAMLドキュメントを読み出す基本
PyYAMLには、YAMLドキュメントをPythonのオブジェクト(リストや辞書)に変換するための関数が数多く含まれている(モジュール名は「yaml」となる)。
- load関数:読み込みに使用するローダーをLoaderパラメーターで指定する必要がある
- safe_load関数:YAML言語仕様のサブセットをサポート。YAMLドキュメント読み出し時に任意のコードの実行が不可能になっているので、安全にYAMLドキュメントを読み出せる
- full_load関数:YAML言語仕様をフルにサポート。ただし、任意のコードの実行は不可能。だが、信頼できないソースからのYAMLドキュメントの読み出しに使用しないこと
- unsafe_load関数:YAMLドキュメントの読み出し時に任意のコードの実行が可能。そのため、この関数を使ってYAMLドキュメントを読み出すのであれば、信頼できるソースからのYAMLドキュメントのみを対象とすること
実際にはsafe_load/full_load/unsafe_load関数は、load関数のLoaderパラメーターにそれぞれSafeLoader/FullLoader/UnsafeLoaderを指定した呼び出すものだと考えればよい。また、1つのYAMLファイルには複数のYAMLドキュメントを格納できるが、上記の関数群は先頭のYAMLドキュメントしか読み出せない。全てのYAMLドキュメントを読み出すには末尾に「_all」を付けたload_all/safe_load_all/full_load_all/unsafe_load_all関数を使用する。
以下は、YAMLファイルからYAMLドキュメントを読み出す例だ。
from yaml import safe_load, unsafe_load, full_load
from pathlib import Path # ファイル内容の確認用
# YAMLファイルからの読み出し
print(Path('test.yaml').read_text())
# 出力結果:
#--- # YAMLドキュメントの開始
#- foo: FOO
#- bar: BAR
#- baz: BAZ
#... # YAMLドキュメントの終了
with open('test.yaml') as f:
data = safe_load(f)
print(data) # [{'foo': 'FOO'}, {'bar': 'BAR'}, {'baz': 'BAZ'}]
上のtest.yamlファイルには3つのマッピングを要素とするシーケンスが記述されている(「-」で始まる3行がシーケンスを表し、その内容である「foo: FOO」などがマッピングを表している)。また、「---」はYAMLドキュメントの開始を、「...」はYAMLドキュメントの終了を意味し、「#」以降はコメントとなっている。
このYAMLファイルをwith文でオープンし、その内容をここではsafe_load関数で読み出している。このときに、マッピングは辞書に、シーケンスはリストに変換される。そのため、safe_load関数で読み出した内容は辞書を要素とするリストになっている。
safe_load関数(その他の関数)はファイルだけではなく、YAML形式で記述された内容の文字列を読み出して、Pythonのオブジェクトに変換することも可能だ。以下に例を示す。
s = """
- foo: FOO # これはYAMLのコメント
- bar: BAR
- baz: BAZ
"""
data = full_load(s)
print(data) # [{'foo': 'FOO'}, {'bar': 'BAR'}, {'baz': 'BAZ'}]
ここでは文字列の内容は先ほどのtest.yamlファイルと同様となっている(YAMLドキュメントを1つだけ含むときには「---」「...」は省略可能)。それを今度はfull_load関数で読み出している。結果は先ほどと同様だ。
上述した通り、safe_load関数はSafeLoaderを、full_load関数はFullLoaderを、unsafe_load関数はUnsafeLoaderを、load関数のLoaderパラメーターに指定するのと同様であるため、以下のような書き方も可能である。
import yaml
from yaml import load
data = load(s, Loader=yaml.UnsafeLoader) # yaml.UnsafeLoaderとyaml.Loaderは同じ
print(data) # [{'foo': 'FOO'}, {'bar': 'BAR'}, {'baz': 'BAZ'}]
以下では、文字列にYAMLドキュメントを記述して、それをsafe_load関数で読み出す形でYAMLドキュメントを読み出すと、それがPythonのオブジェクトにどのように変換されるかを簡単に見ていく。
シーケンス
YAMLのシーケンスとは、0個以上の要素(YAMLではノードと呼ぶ)が順序付きで並んだものと考えられる。シーケンスにはブロックシーケンスとフローシーケンスの2種類の記述方法がある。ブロックシーケンスは「-」で行を始め、そこに1つの要素を記述する。フローシーケンスは「[]」の中に要素をカンマ区切りで並べていく。
PyYAMLではシーケンスはリストに変換される。以下はブロックシーケンスと、それをsafe_load関数でPythonのオブジェクトに読み出す例だ。
s = """
- foo # ブロックシーケンス
- bar
- baz
"""
data0 = safe_load(s) # load(s, Loader=yaml.SafeLoader)
print(data0) # ['foo', 'bar', 'baz']
以下はフローシーケンスをfull_load関数で読み出して、Pythonのオブジェクトに変換する例だ。
s = """
[foo, bar, baz] # フローシーケンス
"""
data1 = full_load(s) # load(s, Loader=yaml.FullLoader)
print(data1) # ['foo', 'bar', 'baz']
下の行の実行結果からは、記法が違っても、上の2つの例で作成した2つのオブジェクト(data0とdata1)が同等であることが分かる。
print(data0 == data1) # True
ブロックシーケンスとフローシーケンスはどちらもネストさせ、シーケンスの要素としてシーケンスを持たせられる。以下に例を示す。
# ネストしたブロックシーケンス
s = """
- L1-1
- # 次レベル
- L2-1
- L2-2
- L1-2 # レベルを元に
- # 次レベル
- # 次レベル
- L3-1
- L3-2
"""
data = safe_load(s)
print(data) # ['L1-1', ['L2-1', 'L2-2'], 'L1-2', [['L3-1', 'L3-2']]]
# ネストしたフローシーケンス
s = """
[L1-1, [L2-1, L2-2], L1-2, [[L3-1, L3-2]]]
"""
data = safe_load(s)
print(data) # ['L1-1', ['L2-1', 'L2-2'], 'L1-2', [['L3-1', 'L3-2']]]
ブロックシーケンスにおいては、「-」とインデントで各要素のネストの仕方が区別される。最初は「-」とインデントによってシーケンスがどのようにネストするかが分かりづらいかもしれないが、これはYAMLファイルを手で書いていくうちに慣れていくだろう。
上の結果を見ると分かるが、ネストしたシーケンスはリストのリストに変換される。
マッピング
YAML仕様ではマッピングとは順序を持たない、キー/値の組みのことで、PyYAMLでは辞書に変換される。マッピングもブロックマッピングとフローマッピングの2種類の記述方法がある。以下にブロックマッピングをsafe_load関数で読み出す例を示す。
s = """
foo: FOO # ブロックマッピング
bar: BAR
baz: BAZ
"""
data = safe_load(s)
print(data) # {'foo': 'FOO', 'bar': 'BAR', 'baz': 'BAZ'}
ブロックマッピングでは1行に「キー: 値」を1つずつ記述していく。一方、フローマッピングでは、「{}」の中に「キー: 値」をカンマ区切りで並べていく。以下に例を示す。
s = """
{foo: FOO, bar: BAR, baz: BAZ} # フローマッピング
"""
data = safe_load(s)
print(data) # {'foo': 'FOO', 'bar': 'BAR', 'baz': 'BAZ'}
シーケンスと同様、マッピングもネストが可能だ。
# ネストしたブロックマッピング(マッピングを値とするマッピング)
s = """
user01:
name: isshiki
id: 100
user02:
name: kawasaki
id: 102
"""
data = safe_load(s)
print(data)
# 出力結果:
#{'user01': {'name': 'isshiki', 'id': 100}, 'user02': {'name': 'kawasaki',
# 'id': 102}}
シーケンスとマッピングの組み合わせ
以下は、シーケンスとマッピングを組み合わせて記述する例だ。
s = """
user01:
- isshiki # ブロックシーケンス
- 100
user02: [kawasaki, 102] # フローシーケンス
"""
data = safe_load(s)
print(data) # {'user01': ['isshiki', 100], 'user02': ['kawasaki', 102]}
この例では、シーケンスをマッピングの値として使用している。これはリストを値とする辞書に変換される。
次にマッピングを要素とするシーケンスの例を示す。
s = """
- foo: FOO
- bar: BAZ
"""
data = safe_load(s)
print(data) # [{'foo': 'FOO'}, {'bar': 'BAZ'}]
s = """
[
foo: FOO,
{bar: BAR}
]
"""
data = safe_load(s)
print(data) # [{'foo': 'FOO'}, {'bar': 'BAR'}]
2つ目の例では、フローシーケンスを改行込みで記述して、その中にマッピングをカンマで並べている点に注目しよう。
スカラー(Unicode文字列)
YAMLにおけるスカラーとは0文字以上のUnicode文字の並びのことだ。スカラーにもブロック形式とフロー形式の記述方法がある。ブロック形式のスカラーでは改行の扱いを指示する「|」と「>」が重要になる。「|」を指定した場合、リテラルスタイルと呼ばれ、改行はそのまま改行として扱われる。「>」を指定した場合、foldedスタイルと呼ばれ、途中の改行は空白文字に変換される。
以下に例を示す。
s = """
forums: | # リテラルスタイル(改行はそのまま)
windows insider
deep insider
foo: > # foldedスタイル(途中の改行は空白文字に変換される)
foo and
bar
"""
data = safe_load(s)
for k, v in data.items():
print(f'{k}: {v}')
# 出力結果:
#forums: windows insider # 改行はそのまま
#deep insider
#
#foo: foo and bar # andの後の改行が半角空白文字に
フロー形式のスカラーではシングルクオートで囲む、ダブルクオートで囲む、どちらも使わない、の3種類の記述方法がある。以下に例を示す。
s = """
single quoted: 'single
quoted scalar'
double quoted: "double
quoted scalar"
"""
data = safe_load(s)
for k, v in data.items():
print(f'{k}: {v}')
# 出力結果:
#single quoted: single quoted scalar
#double quoted: double quoted scalar
YAMLでの明示的な型指定
PyYAMLはYAMLドキュメントに記述された値を自動的にPythonの対応するオブジェクトに変換してくれる。以下に例を示す。
s = """
int: 1 # PyYAMLが型を自動的に変換してくれる
float: 2.0
datetime: 2023-12-26T05:00:00+0900
local datetime: 2023-12-26 05:00:00
date: 2023-12-05
time: 05:00:00
str: string
sequence: [0, 1, 2]
mapping: {foo: FOO, bar: BAR}
"""
data = safe_load(s)
この例では、整数や浮動小数点数、日付、文字列、シーケンス、マッピングが値として記述されている。これらがどんなオブジェクトに変換されるかを見てみよう。
for k, v in data.items():
print(f'{k}: {v},\ttype: {type(v)}')
# 出力結果:
#int: 1, type: <class 'int'>
#float: 2.0, type: <class 'float'>
#datetime: 2023-12-26T05:00:00+0900, type: <class 'str'>
#local datetime: 2023-12-26 05:00:00, type: <class 'datetime.datetime'>
#date: 2023-12-05, type: <class 'datetime.date'>
#time: 05:00:00, type: <class 'str'>
#str: string, type: <class 'str'>
#sequence: [0, 1, 2], type: <class 'list'>
#mapping: {'foo': 'FOO', 'bar': 'BAR'}, type: <class 'dict'>
時刻が文字列になっていることを除けば、多くは問題なく、変換されていることが分かる。が、変換後の型をYAMLドキュメントの側で指定することも可能だ。これにはYAMLのタグと呼ばれる機能を使用する。
型指定に使えるタグにどんなものがあるかについてはPyYAMLのドキュメント「YAML tags and Python types」を参照されたい。YAMLで標準的にサポートされているタグは「!!」に続けて「null」「int」などのデータ型が続き、その後に実際の値を記述する。以下に例を示す。
s = """
none: !!null
int: !!int 20
float: !!float 10
bool: [!!bool true, !!bool false, !!bool yes, !!bool no]
list of tuple: !!omap [{foo: FOO}, {bar: BAR}]
list: !!seq [0, 1, 2]
dict: !!map {foo: FOO, bar: BAR}
"""
data = safe_load(s)
for k, v in data.items():
print(f'{k}: {v}')
# 出力結果:
#none: None
#int: 20
#float: 10.0
#bool: [True, False, True, False]
#list of tuple: [('foo', 'FOO'), ('bar', 'BAR')]
#list: [0, 1, 2]
#dict: {'foo': 'FOO', 'bar': 'BAR'}
文字列sには「!!null」「!!int」などのタグを用いて変換後の型を明示的に与えている。変換後の出力結果を見ると、指定通りに変換できていることが分かる(yes/noがTrue/Falseに変換されている点に注目)。
一方で「YAML tags and Python types」にはPython固有のタグも紹介されている。それらのタグは「!!」に続けて「python/データ型」の形で変換後のデータ型を指定する。以下に例を示す。
s = """
none: !!python/none
bool: [!!python/bool true, !!python/bool off]
bytes: !!python/bytes ZGVlcA==
str: !!python/str python
int: !!python/int 1
float: !!python/float 10
list: !!python/list [foo, bar, baz]
tuple: !!python/tuple [foo, bar, baz]
dict: !!python/dict {foo: FOO, bar: BAR}
"""
data = full_load(s)
for k, v in data.items():
print(f'{k}: {v}')
data = safe_load(s) # yaml.constructor.ConstructorError
data = unsafe_load(s) # OK
標準的なYAMLのタグに対して、bytes型(!!python/bytes)やtuple型などの型指定が可能になっている点に注目されたい。なお、最後の2行にあるように、safe_load関数ではPythonに固有のタグは変換ができない点には注意しよう(full_load関数とunsafe_load関数では変換可能)。
複数のYAMLドキュメントをまとめて読み出す
最後に複数のYAMLドキュメントを読み出す方法を紹介しておこう。既に述べたが、1つのストリーム(ここでは1つのYAMLファイルや1つの文字列だと考えられる)には複数のYAMLドキュメントを格納できる。YAMLドキュメントは「---」で始まり、「...」で終了する。しかし、今までに見てきたsafe_loadなどの関数は複数のYAMLドキュメントを含んでいるストリームには対応していない。その代わりにsafe_load_allなどの関数が用意されている。
例えば、次のように1つの文字列に複数のYAMLドキュメントが書かれていたとする。
s = """
---
foo: FOO
...
---
bar: BAR
...
"""
ここから2つのYAMLドキュメントの内容を読み出すには、例えばsafe_load_all関数が使える。以下に例を示す。
contents = safe_load_all(s)
for idx, content in enumerate(contents, 1):
print(f'{idx}番目のドキュメント')
print(content)
# 出力結果:
#1番目のドキュメント
#{'foo': 'FOO'}
#2番目のドキュメント
#{'bar': 'BAR'}
safe_load_all関数はそれぞれのYAMLドキュメントを指すオブジェクトを反復するジェネレーターを返すので、上の例ではfor文でその内容を取り出している。
ただし、YAMLファイルを扱うときには少し注意が必要だ。例えば、以下のようなtest2.yamlファイルがあったとする。
print(Path('test2.yaml').read_text())
# 出力結果:
# 1つのストリーム(ファイル)に複数のYAMLドキュメント
#---
#foo: FOO # 1つ目のYAMLドキュメント
#...
#---
#bar: BAR # 2つ目のYAMLドキュメント
#...
このYAMLファイルの内容を取り出そうとして、以下のようなコードを書くと例外が発生する。
with open('test2.yaml') as f:
contents = safe_load_all(f)
for content in contents:
print(content) # ValueError: I/O operation on closed file.
これはジェネレーターを介して、YAMLドキュメントの内容をアクセスする前にファイルをクローズしてしまっているからだ。そのため、実際には次のようなコードを書くことになるだろう。
with open('test2.yaml') as f:
contents = safe_load_all(f)
result = [content for content in contents]
for content in result:
print(content)
# 出力結果:
#{'foo': 'FOO'}
#{'bar': 'BAR'}
ここではファイルをクローズする前に、YAMLドキュメントの内容を取得するようなコードとした。
Copyright© Digital Advantage Corp. All Rights Reserved.