我有一个YAML文件,如下所示:
---
!Frog
name: tree frog
colour: green
friends:
- !Frog
name: motorbike frog
- !Frog
name: blue arrow frog
和一个使用PyYAML根据文件创建对象的python程序:
import yaml
class Frog():
def __init__(self, name, colour="", friends=None):
self.name = name
self.colour = colour
self.friends = {}
if friends != None:
for f in friends:
self.friends[f.name] = f
print("{}'s friends: {}".format(self.name, self.friends))
# Constructor for YAML
def frogConstructor(loader, node) :
fields = loader.construct_mapping(node)
return Frog(**fields)
yaml.add_constructor('!Frog', frogConstructor)
f = open("frog.yaml")
loaded = yaml.load(f)
正如您在上面的代码中看到的那样,我正在尝试使用friends参数(其中键是青蛙的名称,值是实际的青蛙对象)创建一个self.friends字典到__init__方法.但是,上面的代码导致以下输出:
tree frog's friends: {}
motorbike frog's friends: {}
blue arrow frog's friends: {}
正如你所看到的,self.friends字典对于所有三只青蛙都是空的,但是树蛙应该有两个朋友.如果我只是让self.friends =朋友,它按预期工作:self.friends是朋友青蛙的列表.我究竟做错了什么?
最佳答案 如果你做self.friends =朋友并不是那么奇怪.您将一个最初为空的列表分配给self.friends,该列表稍后由YAML解析器附加.
如果您希望在构造Frog()之前填充该列表,则必须为construct_mapping()提供deep = True参数,这样做将确保首先创建基础非标量构造以及标量那些.
def frogConstructor(loader, node):
fields = loader.construct_mapping(node, deep=True)
return Frog(**fields)
但是你的代码还有一些问题(它们都没有禁止上面的功能):
>只有一个None,所以如果朋友不是None,那么使用它更合适:比朋友更好!=无:
> yaml.load是不安全的,所以如果你没有完全控制你的输入,那可能意味着擦盘(或更糟). PyYAML没有警告你(在我的ruamel.yaml
解析器中你明确地必须提供不安全的Loader来防止警告消息).
>如果树蛙自恋足以认为自己是自己的朋友,或者如果其中一个朋友认为树蛙是朋友,你可能想要使用锚点和别名来表明(而不仅仅是在不同的地方使用相同的名字) Frog),这对你正在使用的简单构造函数不起作用.
> frogConstructor,作为函数名,不应该是camel case,而是使用frog_constructor.
由于上述原因,我不会使用deep = True参数,而是通过使用两阶段构造函数来获得更安全,更完整的解决方案:
from ruamel import yaml
class Frog():
def __init__(self, name):
self.name = name
def set_values(self, colour="", friends=None):
self.colour = colour
self.friends = {}
if friends is not None:
for f in friends:
self.friends[f.name] = f
print("{}'s friends: {}".format(self.name, self.friends))
def __repr__(self):
return "Frog({})".format(self.name)
# Constructor for YAML
def frog_constructor(loader, node):
fields = loader.construct_mapping(node)
frog = Frog(fields.pop('name'))
yield frog
frog.set_values(**fields)
yaml.add_constructor('!Frog', frog_constructor, yaml.SafeLoader)
f = open("frog.yaml")
loaded = yaml.safe_load(f)
你可以解析这个frog.yaml:
!Frog &tree_frog
name: tree frog
colour: green
friends:
- !Frog
name: motorbike frog
friends:
- *tree_frog
- !Frog
name: blue arrow frog
得到输出:
tree frog's friends: {'blue arrow frog': Frog(blue arrow frog), 'motorbike frog': Frog(motorbike frog)}
motorbike frog's friends: {'tree frog': Frog(tree frog)}
blue arrow frog's friends: {}