无法从 PyYAML 调用的构造函数中的参数构造对象
Can't construct object from parameter in constructor called by PyYAML
我有一个如下所示的 YAML 文件:
---
!Frog
name: tree frog
colour: green
friends:
- !Frog
name: motorbike frog
- !Frog
name: blue arrow frog
以及一个python使用PyYAML根据文件创建对象的程序:
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 = friends
,它会按预期工作:self.friends
是 friend frogs 的列表。我做错了什么?
如果你这样做,事情就会成功 self.friends = friends
并不奇怪。您将一个最初为空的列表分配给 self.friends
,该列表稍后会被 YAML 解析器追加。
如果您希望在构建 Frog()
之前填充该列表,则必须为 construct_mapping()
提供 deep=True
参数,这样做将确保基础 non-scalar 构造和标量构造首先创建。
def frogConstructor(loader, node):
fields = loader.construct_mapping(node, deep=True)
return Frog(**fields)
然而,您的代码还有一些问题(none 其中禁止上述代码运行):
- 只有一个
None
,所以用if friends is not None:
比if friends != None:
更合适
yaml.load
是不安全的,所以如果您不能完全控制您的输入,这可能意味着光盘被擦除(或更糟)。 PyYAML 不会为此警告您(在我的 ruamel.yaml
解析器中,您明确必须提供不安全的 Loader
以防止出现警告消息)。
- 如果
tree frog
自恋到足以将自己视为自己的朋友,或者如果其朋友之一将 tree frog
视为朋友,您可能需要使用锚点和别名来表明这一点(而不仅仅是在不同的 Frog
上使用相同的名称),这不适用于您正在使用的简单构造函数。
frogConstructor
作为函数名,不应该是驼峰式,使用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: {}
我有一个如下所示的 YAML 文件:
---
!Frog
name: tree frog
colour: green
friends:
- !Frog
name: motorbike frog
- !Frog
name: blue arrow frog
以及一个python使用PyYAML根据文件创建对象的程序:
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 = friends
,它会按预期工作:self.friends
是 friend frogs 的列表。我做错了什么?
如果你这样做,事情就会成功 self.friends = friends
并不奇怪。您将一个最初为空的列表分配给 self.friends
,该列表稍后会被 YAML 解析器追加。
如果您希望在构建 Frog()
之前填充该列表,则必须为 construct_mapping()
提供 deep=True
参数,这样做将确保基础 non-scalar 构造和标量构造首先创建。
def frogConstructor(loader, node):
fields = loader.construct_mapping(node, deep=True)
return Frog(**fields)
然而,您的代码还有一些问题(none 其中禁止上述代码运行):
- 只有一个
None
,所以用if friends is not None:
比if friends != None:
更合适
yaml.load
是不安全的,所以如果您不能完全控制您的输入,这可能意味着光盘被擦除(或更糟)。 PyYAML 不会为此警告您(在我的ruamel.yaml
解析器中,您明确必须提供不安全的Loader
以防止出现警告消息)。- 如果
tree frog
自恋到足以将自己视为自己的朋友,或者如果其朋友之一将tree frog
视为朋友,您可能需要使用锚点和别名来表明这一点(而不仅仅是在不同的Frog
上使用相同的名称),这不适用于您正在使用的简单构造函数。 frogConstructor
作为函数名,不应该是驼峰式,使用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: {}