我想知道是否有一种简单的方法可以使用 PyYAML 将包含项目列表的 YAML 文档解析为 python 生成器。
例如,给定文件
# foobar.yaml
---
- foo: ["bar", "baz", "bah"]
something_else: blah
- bar: yet_another_thing
我希望能够做类似的事情
for item in yaml.load_as_generator(open('foobar.yaml')): # does not exist
print(str(item))
我知道有yaml.load_all可以实现类似的功能,但是您需要将每条记录视为自己的文档。我问的原因是因为我有一些非常大的文件,我想将它们转换为 YAML,然后以低内存占用进行解析。
我看了一下 PyYAML 事件 API,但它吓坏了我 =)
我可以理解事件 API 会吓到你,它只会给你带来这么多。首先,您需要跟踪深度(因为您有顶级复杂序列项,以及"bar","baz"等。而且,正确剪切低级序列事件元素后,您必须将它们输入到作曲器中以创建节点(并最终创建 Python 对象),这也不是微不足道的。
但是,由于 YAML 使用缩进,即使对于跨多行的标量,您也可以使用简单的基于行的解析器来识别每个序列元素的开始位置,并一次将它们馈送到正常的 load()
函数中:
#/usr/bin/env python
import ruamel.yaml
def list_elements(fp, depth=0):
buffer = None
in_header = True
list_element_match = ' ' * depth + '- '
for line in fp:
if line.startswith('---'):
in_header = False
continue
if in_header:
continue
if line.startswith(list_element_match):
if buffer is None:
buffer = line
continue
yield ruamel.yaml.load(buffer)[0]
buffer = line
continue
buffer += line
if buffer:
yield ruamel.yaml.load(buffer)[0]
with open("foobar.yaml") as fp:
for element in list_elements(fp):
print(str(element))
导致:
{'something_else': 'blah', 'foo': ['bar', 'baz', 'bah']}
{'bar': 'yet_another_thing'}
我在这里使用了PyYAML的增强版本ruamel.yaml(我是作者),但PyYAML应该以同样的方式工作。