[英]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
。
在汽車的建造過程中,它的乘客還不知道。 因此,您需要做的是在兩步過程中構造潛在的遞歸數據,例如Person
和Car
,首先構造並生成“空” 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.