2012-08-09 67 views
3

我覺得加載一些YAML對象是一個小小的誤解。我在下面定義了這個類。Python/YAML:如何在loadConfig中不僅從YAML文件初始化其他對象?

我想要做的是加載一些對象與覆蓋loadConfig功能爲YAMLObjects。其中一些來自我的.yaml文件,但其他文件應該由從YAML文件加載的對象構建。

例如,在下面的類中,我加載了一個名爲「keep」的成員對象,它是一個字符串,命名了一些要保留在該區域中的項。但是我也想把它解析成一個列表,並將列表存儲爲一個成員對象。而且我不希望用戶必須在YAML中同時提供該參數的字符串和列表版本。

我目前的解決方法是覆蓋Region中的__getattr__函數,並使其創建默認值,如果它看起來沒有找到它們。但是,這僅僅是初始化對象的笨重和複雜。

我在這裏誤解了什麼約定。爲什麼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的殼和探索加載的對象:

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

圖案我發現有用與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 

你會那麼想從序列化有你的地區類繼承,並刪除loadConfig東西。我發佈的代碼檢查構造函數以查看要保存到yaml文件的數據,然後在加載yaml文件時調用具有相同數據集的構造函數。這樣你只需要在你的構造函數中獲得正確的邏輯,並且yaml加載應該可以免費獲得。

該代碼從我的一個項目中被撕掉,如果它不起作用,提前道歉。它也比它需要稍微複雜一點,因爲我想通過使用OrderedDict來控制輸出的順序。你可以用dumper.represent_dict調用我的ordered_dump函數。

+0

我剛剛爲from_yaml()方法實現了這個。它工作得很好,但確實需要你在'__metaclass__ = yaml.YAMLObjectMetaclass'行下面提供yaml_tag。看來(通過查看[source](http://pyyaml.org/browser/pyyaml/trunk/lib/yaml/__init__.py#L282)),如果沒有yaml_tag,to_yaml()和from_yaml()aren不會自動加載。 – 2015-01-26 21:43:34