首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >不能用PyYAML调用的构造函数中的参数构造对象

不能用PyYAML调用的构造函数中的参数构造对象
EN

Stack Overflow用户
提问于 2017-02-14 05:03:43
回答 1查看 725关注 0票数 5

我有一个YAML文件,如下所示:

代码语言:javascript
运行
复制
---
!Frog
    name: tree frog
    colour: green
    friends:
        - !Frog
          name: motorbike frog
        - !Frog
          name: blue arrow frog

和一个使用PyYAML根据文件创建对象的python程序:

代码语言:javascript
运行
复制
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)

正如您在上面的代码中所看到的,我正在尝试从self.friends参数(其中键是青蛙的名称,值是实际的__init__对象)到__init__方法中创建一个__init__字典。但是,上面的代码产生了以下输出:

代码语言:javascript
运行
复制
tree frog's friends: {}
motorbike frog's friends: {}
blue arrow frog's friends: {}

正如你所看到的,self.friends字典对所有三只青蛙都是空的,但是树蛙应该有两个朋友。如果我只做self.friends = friends,它就会像预期的那样工作:self.friends是朋友青蛙的列表。我做错了什么?

EN

Stack Overflow用户

回答已采纳

发布于 2017-02-14 07:52:21

如果你做了self.friends = friends,事情就不会那么奇怪了。将初始空列表分配给self.friends,该列表随后由YAML解析器追加。

如果希望在构建Frog()之前填充该列表,则必须为construct_mapping()提供deep=True参数,这样做将确保首先创建基础的非标量结构以及标量结构。

代码语言:javascript
运行
复制
def frogConstructor(loader, node):
    fields = loader.construct_mapping(node, deep=True)
    return Frog(**fields)

但是,您的代码还存在一些问题(但是,没有一个问题禁止上述功能):

  • 只有一个None,所以使用if friends is not None:比使用if friends != None:更合适。
  • yaml.load是不安全的,所以如果您无法完全控制输入,这可能意味着删除磁盘(或更糟)。PyYAML不会为此发出警告(在我的ruamel.yaml解析器中,您必须显式地提供不安全的Loader以防止发出警告消息)。
  • 如果tree frog自恋到认为自己是它自己的朋友,或者如果它的一个朋友认为tree frog是朋友,那么您可能需要使用锚和别名来表示这一点(而不仅仅是在不同的Frog上使用相同的名称),这将不适用于您正在使用的简单构造函数。
  • frogConstructor作为函数名,不应该是camel case,而是使用frog_constructor

由于上述原因,我不会使用deep=True参数,而是使用一个更安全、更完整的解决方案,方法是使用两个阶段的构造函数:

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
!Frog &tree_frog
    name: tree frog
    colour: green
    friends:
        - !Frog
          name: motorbike frog
          friends:
            - *tree_frog
        - !Frog
          name: blue arrow frog

作为输出:

代码语言:javascript
运行
复制
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: {}
票数 3
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42218249

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档