簡體   English   中英

無法從 YAML 創建 Python 對象數組

[英]Unable to create an array of Python objects from YAML

我試圖從 YAML 中實例化一個 python 對象數組,在一個結構中。 在結構之外,我可以輕松地做到這一點,但似乎 YAML BaseLoader 無法遞歸搜索我的對象的子節點。

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)])]

無論如何,即使是字符串數組,鍵“Passengers”的關聯值始終是字典 m 中的 []。

我是否必須手動告訴構造函數首先在 from_yaml 函數中遍歷節點的其余部分,或者 YAML 加載器是否從下往上遞歸地工作?

我能夠在這篇文章中找到部分答案。

對於raumel.yaml ,看起來我們的構造函數是from_yaml函數,它是在注冊類時添加的。 我們所要做的就是在from_yaml中初始化我們的類之后,在我們檢索遞歸項Passengers之前添加一個yield。

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})'

在您的示例中無需import yaml

在汽車的建造過程中,它的乘客還不知道。 因此,您需要做的是在兩步過程中構造潛在的遞歸數據,例如PersonCar ,首先構造並生成“空” Car ,然后在已生成的實例上填充Driver和任何Passangers 加載器知道如何處理這個問題,因此您不必遞歸到from_yaml中的任何內容。

此外,您需要在from_yaml中調用constructor.construct_mapping(node, deep=True) ,而不是在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}')

這使:

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)])]

雖然允許在錨之后寫標記,但 IMO 更適合在標記之后寫錨,因為您將獲得標記對象的錨定實例。

所以這讓我想知道&2 !Person的名字是什么(可能與!Person &2相同),是皮特嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM