gpt4 book ai didi

python - 为什么 PyYAML 使用生成器来构造对象?

转载 作者:太空狗 更新时间:2023-10-29 17:47:58 24 4
gpt4 key购买 nike

我一直在阅读 PyYAML 源代码,以尝试了解如何定义可以使用 add_constructor 添加的适当构造函数。我现在非常了解该代码的工作原理,但我仍然不明白为什么 SafeConstructor 中的默认 YAML 构造函数是生成器。例如SafeConstructor的方法construct_yaml_map:

def construct_yaml_map(self, node):
data = {}
yield data
value = self.construct_mapping(node)
data.update(value)

我理解生成器是如何在 BaseConstructor.construct_object 中使用的,如下所示,如果 deep=False 被传递,则生成一个对象并只用来自节点的数据填充它construct_mapping:

    if isinstance(data, types.GeneratorType):
generator = data
data = generator.next()
if self.deep_construct:
for dummy in generator:
pass
else:
self.state_generators.append(generator)

而且我了解在 construct_mappingdeep=False 情况下如何在 BaseConstructor.construct_document 中生成数据。

def construct_document(self, node):
data = self.construct_object(node)
while self.state_generators:
state_generators = self.state_generators
self.state_generators = []
for generator in state_generators:
for dummy in generator:
pass

我不明白的是,通过遍历 construct_document 中的生成器,将数据对象 stub 并向下遍历对象的好处。是否必须这样做以支持 YAML 规范中的某些内容,或者它是否提供了性能优势?

This answer on another question有点帮助,但我不明白为什么这个答案会这样:

def foo_constructor(loader, node):
instance = Foo.__new__(Foo)
yield instance
state = loader.construct_mapping(node, deep=True)
instance.__init__(**state)

而不是这个:

def foo_constructor(loader, node):
state = loader.construct_mapping(node, deep=True)
return Foo(**state)

我已经测试过后一种形式适用于其他答案中发布的示例,但也许我遗漏了一些边缘情况。

我使用的是 3.10 版的 PyYAML,但看起来问题代码在最新版本 (3.12) 的 PyYAML 中是相同的。

最佳答案

在 YAML 中你可以有 anchors and aliases .有了它,您可以直接或间接地创建自引用结构。

如果 YAML 没有这种自引用的可能性,您可以先构造所有子结构,然后一次性创建父结构。但是由于 self 引用,您可能还没有让 child “填写”您正在创建的结构。通过使用生成器的两步过程(我称此为两步,因为在方法结束之前它只有一个 yield),您可以部分创建一个对象并用自引用填充它,因为对象存在(即它在内存中的位置已定义)。

好处不在于速度,而纯粹是因为使自引用成为可能。

如果您从您引用的答案中简化示例,则会加载以下内容:

import sys
import ruamel.yaml as yaml


class Foo(object):
def __init__(self, s, l=None, d=None):
self.s = s
self.l1, self.l2 = l
self.d = d


def foo_constructor(loader, node):
instance = Foo.__new__(Foo)
yield instance
state = loader.construct_mapping(node, deep=True)
instance.__init__(**state)

yaml.add_constructor(u'!Foo', foo_constructor)

x = yaml.load('''
&fooref
!Foo
s: *fooref
l: [1, 2]
d: {try: this}
''', Loader=yaml.Loader)

yaml.dump(x, sys.stdout)

但是如果您将 foo_constructor() 更改为:

def foo_constructor(loader, node):
instance = Foo.__new__(Foo)
state = loader.construct_mapping(node, deep=True)
instance.__init__(**state)
return instance

(移除产量,添加最终返回),你得到一个 ConstructorError: with as message

found unconstructable recursive node 
in "<unicode string>", line 2, column 1:
&fooref

PyYAML 应该给出类似的信息。检查该错误的回溯,您可以看到 ruamel.yaml/PyYAML 尝试在源代码中解析别名的位置。

关于python - 为什么 PyYAML 使用生成器来构造对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41900782/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com