无法从yaml创建一系列Python对象

发布于 2025-02-06 04:54:53 字数 2407 浏览 2 评论 0原文

我正在尝试在结构内实例化YAML的一系列Python对象。在结构之外,我可以轻松地执行此操作,但是似乎YAML基本装载机未能递归地搜索我对象的子节点。

import yaml
import ruamel.yaml

class Person:
    def __init__(self, name: str = 'JohnDoe'):
        self.name = name

    @classmethod
    def from_yaml(cls, constructor, node):
        for m in constructor.construct_yaml_map(node):
            pass
        if 'Name' in m:
            name = m['Name']

        return cls(name=name)

    def __repr__(self):
        return f'Person(name={self.name})'


class Car:
    def __init__(self):
        self.passengers = []

    def add_person(self, person: Person = None):
        self.passengers.append(person)

    @classmethod
    def from_yaml(cls, constructor, node):
        for m in constructor.construct_yaml_map(node):
            pass

        inst = cls()

        if 'Driver' in m:
            inst.passengers = [m['Driver']]+inst.passengers

        if 'Passengers' in m:
            foo = m['Passengers']
            print(f'm[\'Passengers\'] = {foo}')
            for person in m['Passengers']:
                inst.add_person(person)

        return inst

    def __repr__(self):
        return f'Car(passengers={self.passengers})'


if __name__ == "__main__":

    yaml = ruamel.yaml.YAML(typ='safe')
    yaml.register_class(Person)
    yaml.register_class(Car)

    data = yaml.load("""
        - !Person &0
            Name: 'Paul'

        - !Person &1
            Name: 'George'
            
        - !Person &3
            Name: 'John'

        - !Car
            Driver: *0
            Passengers: [*1]

        - !Car
            Driver: *3
            Passengers: 
                - !Person &4
                    Name: 'Ringo'

        """)

    print(f'data = {data}')

上面的代码在执行时将以下代码打印到控制台:

m['Passengers'] = []
m['Passengers'] = []
data = [Person(name=Paul), Person(name=George), Person(name=John), Car(passengers=[Person(name=Paul)]), Car(passengers=[Person(name=John)])]

我希望输出无论如何

m['Passengers'] = [Person(name=George)]
m['Passengers'] = [Person(name=Ringo)]
data = [Person(name=Paul), Person(name=George), Person(name=John), Car(passengers=[Person(name=Paul), Person(name=George)]), Car(passengers=[Person(name=John), Person(name=Ringo)])]

,即使有一系列字符串,键“乘客”的关联值始终在字典中[]。

我是否必须手动告诉构造函数首先在from_yaml函数中遍历节点的其余部分,还是yaml加载程序从底部递归工作?

I am trying to instantiate an array of python objects from YAML, inside a struct. Outside of a struct I am able to do this easily, but it seems that the YAML BaseLoader is failing to recursively search the sub nodes of my object.

import yaml
import ruamel.yaml

class Person:
    def __init__(self, name: str = 'JohnDoe'):
        self.name = name

    @classmethod
    def from_yaml(cls, constructor, node):
        for m in constructor.construct_yaml_map(node):
            pass
        if 'Name' in m:
            name = m['Name']

        return cls(name=name)

    def __repr__(self):
        return f'Person(name={self.name})'


class Car:
    def __init__(self):
        self.passengers = []

    def add_person(self, person: Person = None):
        self.passengers.append(person)

    @classmethod
    def from_yaml(cls, constructor, node):
        for m in constructor.construct_yaml_map(node):
            pass

        inst = cls()

        if 'Driver' in m:
            inst.passengers = [m['Driver']]+inst.passengers

        if 'Passengers' in m:
            foo = m['Passengers']
            print(f'm[\'Passengers\'] = {foo}')
            for person in m['Passengers']:
                inst.add_person(person)

        return inst

    def __repr__(self):
        return f'Car(passengers={self.passengers})'


if __name__ == "__main__":

    yaml = ruamel.yaml.YAML(typ='safe')
    yaml.register_class(Person)
    yaml.register_class(Car)

    data = yaml.load("""
        - !Person &0
            Name: 'Paul'

        - !Person &1
            Name: 'George'
            
        - !Person &3
            Name: 'John'

        - !Car
            Driver: *0
            Passengers: [*1]

        - !Car
            Driver: *3
            Passengers: 
                - !Person &4
                    Name: 'Ringo'

        """)

    print(f'data = {data}')

the above code prints the following to the console on execution:

m['Passengers'] = []
m['Passengers'] = []
data = [Person(name=Paul), Person(name=George), Person(name=John), Car(passengers=[Person(name=Paul)]), Car(passengers=[Person(name=John)])]

where as I would expect the output to be

m['Passengers'] = [Person(name=George)]
m['Passengers'] = [Person(name=Ringo)]
data = [Person(name=Paul), Person(name=George), Person(name=John), Car(passengers=[Person(name=Paul), Person(name=George)]), Car(passengers=[Person(name=John), Person(name=Ringo)])]

no matter what, even with an array of strings, the associated value of the key 'Passengers' is always [] in the dictionary m.

do I have to manually tell the constructor to travers the rest of the node first in the from_yaml function, or does YAML loader work recursively from the bottom up?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

时光无声 2025-02-13 04:54:53

在您的示例中,无需导入yaml

在汽车建造过程中,其乘客尚不知道。所以
您需要做的是构建潜在的递归数据
作为PersonCAR在两个步骤过程中,首先构建并产生“空” car
然后在已经产生的实例上填写驱动程序和任何passangers。装载机知道
如何处理此操作,因此您不必在from_yaml中反复出现。

此外,您需要调用constructor.construct_mapping(node,deep = true)
from_yaml中,而不是您通过structionor.conscruct_yaml_map(node)

import ruamel.yaml

class Person:
    def __init__(self, name: str = 'JohnDoe'):
        self.name = name

    @classmethod
    def from_yaml(cls, constructor, node):
        inst = cls()
        yield inst
        m = constructor.construct_mapping(node, deep=True)
        if 'Name' in m:
            inst.name = m['Name']
     
    def __repr__(self):
        return f'Person(name={self.name})'


class Car:
    def __init__(self):
        self.passengers = []

    def add_person(self, person: Person = None):
        self.passengers.append(person)

    @classmethod
    def from_yaml(cls, constructor, node):
        inst = cls()
        yield inst
        m = constructor.construct_mapping(node, deep=True)

        if 'Driver' in m:
            inst.passengers = [m['Driver']] + inst.passengers

        if 'Passengers' in m:
            foo = m['Passengers']
            print(f'm[\'Passengers\'] = {foo}')
            for person in m['Passengers']:
                inst.add_person(person)

    def __repr__(self):
        return f'Car(passengers={self.passengers})'


if __name__ == "__main__":

    yaml = ruamel.yaml.YAML(typ='safe')
    yaml.register_class(Person)
    yaml.register_class(Car)

    data = yaml.load("""
        - !Person &0
            Name: 'Paul'

        - !Person &1
            Name: 'George'
            
        - !Person &3
            Name: 'John'

        - !Car
            Driver: *0
            Passengers: [*1]

        - !Car
            Driver: *3
            Passengers: 
                - !Person &4
                    Name: 'Ringo'

        """)

    print(f'data = {data}')

whove:disv:

m['Passengers'] = [Person(name=George)]
m['Passengers'] = [Person(name=Ringo)]
data = [Person(name=Paul), Person(name=George), Person(name=John), Car(passengers=[Person(name=Paul), Person(name=George)]), Car(passengers=[Person(name=John), Person(name=Ringo)])]

whit the the标签跟随锚,它更合适,但它更合适,但它更合适写锚
其次是标签,因为您将获得标记对象的锚定实例。

因此,这让我想知道& 2!person>的名称是什么(可能与!person& 2相同),是Pete吗?

There is no need to import yaml in your example.

During the construction of the car, its passengers are not yet know. So
what you need to do is construct potentially recursive data such
as Person and Car in a two step process, first constructing and yielding the "empty" Car
then filling in the Driver and any Passangers on the already yielded instance. The loader knows
how to handle this, so you don't have to recurse into anything in your from_yaml.

Additionally you'll need to call constructor.construct_mapping(node, deep=True)
in from_yaml, instead of your iteration over constructor.construct_yaml_map(node):

import ruamel.yaml

class Person:
    def __init__(self, name: str = 'JohnDoe'):
        self.name = name

    @classmethod
    def from_yaml(cls, constructor, node):
        inst = cls()
        yield inst
        m = constructor.construct_mapping(node, deep=True)
        if 'Name' in m:
            inst.name = m['Name']
     
    def __repr__(self):
        return f'Person(name={self.name})'


class Car:
    def __init__(self):
        self.passengers = []

    def add_person(self, person: Person = None):
        self.passengers.append(person)

    @classmethod
    def from_yaml(cls, constructor, node):
        inst = cls()
        yield inst
        m = constructor.construct_mapping(node, deep=True)

        if 'Driver' in m:
            inst.passengers = [m['Driver']] + inst.passengers

        if 'Passengers' in m:
            foo = m['Passengers']
            print(f'm[\'Passengers\'] = {foo}')
            for person in m['Passengers']:
                inst.add_person(person)

    def __repr__(self):
        return f'Car(passengers={self.passengers})'


if __name__ == "__main__":

    yaml = ruamel.yaml.YAML(typ='safe')
    yaml.register_class(Person)
    yaml.register_class(Car)

    data = yaml.load("""
        - !Person &0
            Name: 'Paul'

        - !Person &1
            Name: 'George'
            
        - !Person &3
            Name: 'John'

        - !Car
            Driver: *0
            Passengers: [*1]

        - !Car
            Driver: *3
            Passengers: 
                - !Person &4
                    Name: 'Ringo'

        """)

    print(f'data = {data}')

which gives:

m['Passengers'] = [Person(name=George)]
m['Passengers'] = [Person(name=Ringo)]
data = [Person(name=Paul), Person(name=George), Person(name=John), Car(passengers=[Person(name=Paul), Person(name=George)]), Car(passengers=[Person(name=John), Person(name=Ringo)])]

Although it is allowed to write the tag followed by the anchor, it is IMO more appropriate to write the anchor
followed by the tag, because you'll get an anchored instance of the tagged object.

So that leaves me wondering what the name is of &2 !Person (probably the same as for !Person &2), is it Pete?

放低过去 2025-02-13 04:54:53

我能够在中找到部分答案。 /a>。

raumel.yaml的情况下,看来我们的构造函数是from_yaml函数,在注册课时会添加。我们要做的就是在from_yaml初始化课程之后添加产量,然后在我们检索递归项目乘客之前。

class Car:
    def __init__(self):
        self.passengers = []

    def add_person(self, person: Person = None):
        self.passengers.append(person)

    @classmethod
    def from_yaml(cls, constructor, node):

        for m in constructor.construct_yaml_map(node):
            print(f'm{type(m)} = {m}')
            pass

        inst = cls()
        yield inst # <-- This yield statement fixes our issue

        if 'Driver' in m:
            inst.passengers = [m['Driver']]+inst.passengers

        if 'Passengers' in m:
            foo = m['Passengers']
            print(f'm[\'Passengers\'] = {foo}')
            for person in m['Passengers']:
                inst.add_person(person)
                
        return inst

    def __repr__(self):
        return f'Car(passengers={self.passengers})'

I was able to find a partial answer in this post.

In the case of raumel.yaml, it looks like our constructor is the from_yaml function, which gets added when the class is registered. All we have to do is add a yield after the initialization of our class in from_yaml, and before we retrieve our recursive item Passengers.

class Car:
    def __init__(self):
        self.passengers = []

    def add_person(self, person: Person = None):
        self.passengers.append(person)

    @classmethod
    def from_yaml(cls, constructor, node):

        for m in constructor.construct_yaml_map(node):
            print(f'm{type(m)} = {m}')
            pass

        inst = cls()
        yield inst # <-- This yield statement fixes our issue

        if 'Driver' in m:
            inst.passengers = [m['Driver']]+inst.passengers

        if 'Passengers' in m:
            foo = m['Passengers']
            print(f'm[\'Passengers\'] = {foo}')
            for person in m['Passengers']:
                inst.add_person(person)
                
        return inst

    def __repr__(self):
        return f'Car(passengers={self.passengers})'

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文