Why does PyYAML use generators to construct objects?

后端 未结 1 1853
悲&欢浪女
悲&欢浪女 2020-12-11 02:11

I\'ve been reading the PyYAML source code to try to understand how to define a proper constructor function that I can add with add_constructor. I have a pretty

相关标签:
1条回答
  • 2020-12-11 02:40

    In YAML you can have anchors and aliases. With that you can make self-referential structures, directly or indirectly.

    If YAML would not have this possibility of self-reference, you could just first construct all the children and then create the parent structure in one go. But because of the self-references you might not have the child yet to "fill-out" the structure that you are creating. By using the two-step process of the generator (I call this two step, because it has only one yield before you come to the end of the method), you can create an object partially and the fill it out with a self-reference, because the object exist (i.e. its place in memory is defined).

    The benefit is not in speed, but purely because of making the self-reference possible.

    If you simplify the example from the answer you refer to a bit, the following loads:

    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)
    

    but if you change foo_constructor() to:

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

    (yield removed, added a final return), you get a ConstructorError: with as message

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

    PyYAML should give a similar message. Inspect the traceback on that error and you can see where ruamel.yaml/PyYAML tries to resolve the alias in the source code.

    0 讨论(0)
提交回复
热议问题