2016-05-16 84 views
4

我有下面的代码对象(PyYAML Python3):YAML解析到

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 

的问题是,当我加载settingssettings.daily ISN” t集。无论我是否在__init__中这么说。

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

我在做什么错?

+0

而第二个答案似乎更容易对我来说,第一个是更全面的,包括宝贵的数据。 – Srgrn

+0

@Srgrn哪一个是第一个,哪个是第二个? – Anthon

+0

愚蠢的我。你是第一个。 – Srgrn

回答

0

的Python PyYAML中的对象是在两个步骤中构建的。首先调用__new__(在Constructor.make_python_instance()中),然后设置属性(在Constructor.set_python_instance_state()中)。由于YAML支持对对象的引用,并且该对象(间接)是自引用,所以它不能一次构建,因为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,那么您应该将它缓存在eg 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 
+0

我已经使用了你的解决方案,但是更进了一步,并且删除了__init __(),因为尽我所知,它从未被PyYAML调用过。我有什么理由离开它? – cjm2671

+0

@ cjm2671如果你想在测试例程中实例化类的对象,它有助于获得'__init __()',但是你并不需要拥有它。 – Anthon

1

一种可能性是写一个constructorSettings

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 
+0

你不必写一个构造函数。 PyYAML具有构建机制('__setstate__',子类化'yaml.YAMLObject')来处理对象加载。 – Anthon