3

我认为加载一些 YAML 对象是一个小误解。我定义了下面的类。

我想要做的是加载一些具有覆盖loadConfig函数的对象YAMLObjects。其中一些来自我的.yaml文件,但其他应该由从 YAML 文件加载的对象构建而成。

例如,在下面的类中,我加载了一个名为“keep”的成员对象,它是一个字符串,用于命名要保留在该区域中的一些项目。但我也想将其解析为一个列表,并将该列表也存储为成员对象。而且我不希望用户必须在 YAML 中同时提供此参数的字符串和列表版本。

我目前的工作是覆盖__getattr__里面的函数Region,如果它看起来没有找到它们,让它创建默认值。但这比初始化对象所需要的笨重和复杂。

我在这里误解了什么约定。为什么该loadConfig方法不创建 YAML 中没有的其他内容?

import yaml, pdb

class Region(yaml.YAMLObject):
    yaml_tag = u'!Region'

    def __init__(self, name, keep, drop):
        self.name = name
        self.keep = keep
        self.drop = drop

        self.keep_list = self.keep.split("+")
        self.drop_list = self.drop.split("+")
        self.pattern = "+".join(self.keep_list) + "-" + "-".join(self.drop_list)
    ###

    def loadConfig(self, yamlConfig):
        yml = yaml.load_all(file(yamlConfig))
        for data in yml:

            # These get created fine
            self.name = data["name"]
            self.keep = data["keep"]
            self.drop = data["drop"]

            # These do not get created.
            self.keep_list = self.keep.split("+")
            self.drop_list = self.drop.split("+")
            self.pattern = "+".join(self.keep_list) + "-" + "-".join(self.drop_list)
    ###  
### End Region

if __name__ == "__main__":
    my_yaml = "/home/path/to/test.yaml"
    region_iterator = yaml.load_all(file(my_yaml))

    # Set a debug breakpoint to play with region_iterator and
    # confirm the extra stuff isn't created.
    pdb.set_trace()

在这里test.yaml,你可以运行所有这些,看看我的意思:

 Regions:

   # Note: the string conventions below are for an
   # existing system. This is a shortened, representative
   # example.

   Market1:  
    !Region                 
        name: USAndGB
        keep: US+GB
        drop: !!null 

   Market2:
    !Region
        name: CanadaAndAustralia
        keep: CA+AU
        drop: !!null 

例如,这里是我在 IPython shell 中运行它并探索加载的对象时的样子:

In [57]: %run "/home/espears/testWorkspace/testRegions.py"
--Return--
> /home/espears/testWorkspace/testRegions.py(38)<module>()->None
-> pdb.set_trace()
(Pdb) region_iterator
<generator object load_all at 0x1139d820>
(Pdb) tmp = region_iterator.next()
(Pdb) tmp
{'Regions': {'Market2': <__main__.Region object at 0x1f858550>, 'Market1': <__main__.Region object at 0x11a91e50>}}
(Pdb) us = tmp['Regions']['Market1']
(Pdb) us
<__main__.Region object at 0x11a91e50>
(Pdb) us.name
'USAndGB'
(Pdb) us.keep
'US+GB'
(Pdb) us.keep_list
*** AttributeError: 'Region' object has no attribute 'keep_list'
4

1 回答 1

4

我发现对基本上是存储的类使用 yaml 很有用的一种模式是让加载器使用构造函数,以便以与正常创建对象时相同的方式创建对象。如果我理解您正在尝试正确执行的操作,那么这种结构可能会很有用:

import inspect
import yaml
from collections import OrderedDict

class Serializable(yaml.YAMLObject):
    __metaclass__ = yaml.YAMLObjectMetaclass
    @property
    def _dict(self):
        dump_dict = OrderedDict()

        for var in inspect.getargspec(self.__init__).args[1:]:
            if getattr(self, var, None) is not None:
                item = getattr(self, var)
                if isinstance(item, np.ndarray) and item.ndim == 1:
                    item = list(item)
                dump_dict[var] = item

        return dump_dict

    @classmethod
    def to_yaml(cls, dumper, data):
        return ordered_dump(dumper, '!{0}'.format(data.__class__.__name__), 
                            data._dict)


    @classmethod
    def from_yaml(cls, loader, node):
        fields = loader.construct_mapping(node, deep=True)
        return cls(**fields)

def ordered_dump(dumper, tag, data):
    value = []
    node = yaml.nodes.MappingNode(tag, value)
    for key, item in data.iteritems():
        node_key = dumper.represent_data(key)
        node_value = dumper.represent_data(item)
        value.append((node_key, node_value))

    return node

然后,您希望您的 Region 类继承自 Serializable,并删除 loadConfig 的东西。我发布的代码检查构造函数以查看要保存到 yaml 文件的数据,然后在加载 yaml 文件时使用相同的数据集调用构造函数。这样,您只需在构造函数中正确获取逻辑,并且 yaml 加载应该免费获得它。

该代码是从我的一个项目中提取的,如果它不能正常工作,请提前道歉。它也比它需要的稍微复杂一些,因为我想通过使用 OrderedDict 来控制输出的顺序。你可以用对 dumper.represent_dict 的调用来替换我的 ordered_dump 函数。

于 2013-02-19T14:34:25.960 回答