【问题标题】:YAML parsing to Objects (PyYAML Python3)YAML 解析为对象 (PyYAML Python3)
【发布时间】:2016-09-12 14:05:23
【问题描述】:

我有以下代码:

class Settings:
    def __init__(self, annual_volatility_target):
        self.annual_volatility_target = annual_volatility_target
        self.daily = annual_volatility_target/np.sqrt(252)

def yaml_load(name):
    with open('yaml/' + str(name) + '.yaml', 'r') as ymlfile:
        return yaml.load(ymlfile)

settings = yaml_load("settings")

使用以下 YAML:

!!python/object:v.Settings
annual_volatility_target: 0.25

问题是,当我加载settings 时,settings.daily 没有设置。 settings.annual_volatility_target 是,不管我在__init__ 中是否这样说。

如果我手动实例化 Settings 对象(即不使用 PyYAML),它可以正常工作。

我做错了什么?

【问题讨论】:

  • 虽然第二个答案对我来说似乎更容易,但第一个答案更完整并且包含有价值的数据。
  • @Srgrn 哪个是第一个,哪个是第二个?
  • 我太傻了。你是第一个。

标签: python pyyaml


【解决方案1】:

一种可能性是为Settings 写一个constructor

import yaml
import numpy as np

class Settings:
    def __init__(self, annual_volatility_target):
        self.annual_volatility_target = annual_volatility_target
        self.daily = annual_volatility_target/np.sqrt(252)

def yaml_load(name):
    with open(str(name) + '.yaml', 'r') as ymlfile:
        return yaml.load(ymlfile)

def settings_constructor(loader, node):
    fields = loader.construct_mapping(node)
    return Settings(fields['annual_volatility_target'])

yaml.add_constructor('!v.Settings', settings_constructor)

settings = yaml_load("settings")

print(settings.annual_volatility_target)
print(settings.daily)

我必须使用修改后的 yaml 文件(我无法使用注释 !!python/object:v.Settings 使其工作):

!v.Settings
annual_volatility_target: 0.25

【讨论】:

  • 您不必编写构造函数。 PyYAML 也有内置机制(__setstate__,子类化yaml.YAMLObject)来处理对象加载。
【解决方案2】:

PyYAML 中的 Python 对象是通过两步过程构建的。首先调用__new__(在Constructor.make_python_instance() 中),然后设置属性(在Constructor.set_python_instance_state() 中)。之所以需要这两个步骤,是因为 YAML 支持对对象的引用,如果该对象是(间接)自引用的,则无法一次性构造它,因为它所依赖的参数(包括其自身)尚不可用。

您可以通过两种方式解决此问题。您可以为Settings 定义__setstate__(),这将使用dict 调用,也可以从__init__() 调用:

import yaml

yaml_str = """\
!!python/object:try.Settings
annual_volatility_target: 0.25
"""

class Settings:
    def __init__(self, annual_volatility_target):
        self.__setstate__({annual_volatility_target: annual_volatility_target})

    def __setstate__(self, kw):
        self.annual_volatility_target = kw.get('annual_volatility_target')
        self.daily = self.annual_volatility_target/np.sqrt(252)

    def __repr__(self):
        return "Setting({}, {})".format(self.annual_volatility_target, self.daily)

settings = yaml.load(yaml_str)

print(settings)

另一个更通用(非 PyYAML)的解决方案是在首次访问时创建 daily 值:

class Settings:
    def __init__(self, annual_volatility_target):
        self.annual_volatility_target = annual_volatility_target

    @property:
    def daily(self):
         return annual_volatility_target/np.sqrt(252)

如果您经常访问daily,那么您应该将其缓存在例如self._daily第一次计算值:

class Settings:
    def __init__(self, annual_volatility_target):
        self.annual_volatility_target = annual_volatility_target
        self._daily = None

    @property:
    def daily(self):
         if self._daily is None:  
             self._daily = annual_volatility_target/np.sqrt(252)
         return self._daily

【讨论】:

  • 我使用了您的解决方案,但更进一步,删除了 __init__(),因为据我所知,PyYAML 从不调用它。有什么理由让我把它留在里面吗?
  • @cjm2671 如果您想在测试例程等中实例化类的对象,拥有__init__() 会有所帮助。但您实际上并不一定要拥有它。
猜你喜欢
  • 2021-05-17
  • 2011-01-27
  • 2023-04-01
  • 2019-02-13
  • 2021-04-01
  • 2020-01-16
  • 2012-07-20
  • 2013-04-25
  • 2023-03-11
相关资源
最近更新 更多