2016-04-30 91 views
0

我正在努力與PyYAML文檔瞭解一個可能很容易的事情。 我有一個字符串名稱映射到Python對象的字典:pyyaml地圖字典爲對象字典

lut = { 'bar_one': my_bar_one_obj, 
     'bar_two': my_bar_two_obj } 

,我想加載這樣的YAML文件和所有「富」節點映射到我的字典對象(反傾銷,是不是真的有必要)

node1: 
    # ... 
    foo: "bar_one" 
node2: 
    # ... 
    foo: "bar_two" 

我首先想到的是使用add_constructor,但我無法找到一個方法來給它一個額外的kwarg。也許是一個自定義的加載 PyYAML文檔是不是真的有幫助或者可能我找錯了關鍵字...

我可以接受使用自定義標籤一樣

node1: 
    # ... 
    foo: !footag "bar_one" 
node2: 
    # ... 
    foo: !footag "bar_two" 

但是檢測只是foo節點會更好

回答

2

你不是在尋找錯誤的關鍵字,這不是我所知道的任何YAML解析器。 YAML解析器加載一個自包含的可能複雜的數據結構。而你想要做的就是在一個分析步驟中將自包含的結構合併到一個已經存在的結構中(lut)。解析器是建立通過提供替代程序允許的調整不提供程序+數據

有針對內置PyYAML任何選項,即沒有內置的方式來告訴裝載機約lut那使PyYAML對它做某些事情,當然不要將鍵值對(假設這就是你所說的節點)作爲鍵的值。

想要得到你想要的最簡單的方法是使用一些後處理,它需要從你的YAML文件(這也是一個字典)加載lut和數據並將它們結合起來。

如果您想嘗試,並與add_constructor做到這一點,那麼你需要做的是建立一個類與__call__方法,與lut創建類的實例作爲論據,比傳遞例如作爲替代構造):如果你希望你的 構造函數來處理(所有的)正常類型的字典圖」:

class ConstructorWithLut: 
    def __init__(self, lut): 
     self._lut = lut 

    def __call__(self): 
     # the actual constructor routine added by add_constructor 

constructor_with_lut(lut) 
SomeConstructor.add_constructor('your_tag', constructor_with_lut) 

在其中您可以替換 'your_tag' 與u'tag:yaml.org,2002。

另一個選擇是在YAML加載過程中執行此操作,但是您再次不能只調整Loader或其組成部分之一(Constructor),因爲您通常在類中不是對象。你需要一個能夠附加lut的對象。所以你要做的是創建你自己的構造函數和你自己的裝載器,它使用這個構造函數,然後替換實例化你的裝載器,附加lut(只需將它添加爲一個獨特的屬性,或通過將其作爲參數傳入並把它交給你的構造函數)。

你的構造,這應該是現有的構造函數中的一個子類,則必須有自己的construct_mapping()首先調用父類的construct_mapping(),並返回結果之前,檢查它是否可以更新屬性到lut已被分配。您不能根據查看foo的字典的鍵來執行此操作,因爲如果您有這樣的密鑰,則無法訪問需要分配給lut的父節點。您需要做的是查看映射的任何值是否爲具有密鑰名稱foo的字典,如果符合,字典可用於根據與foo關聯的值更新lut

def update_from_yaml(your_dict, yaml_data): 
    for node_key in yaml_data: 
     node_value = yaml_data[node_key] 
     map_value(your_dict, node_key, node_value) 

def map_value(your_dict, key, value): 
    foo_val = value.get('foo') 
    if foo_val is None: # key foo not found 
     return 
    your_dict[foo_val] = value # or = {key: value} 

我不知道你真的有「轉讓所有富節點」的意思是,YAML數據具有在頂層沒有節點:

使用兩個程序,我一定會先執行後處理階段,它只有鍵和值。所以你要麼分配那一對,要麼只分配它的值(一個字典)。

一旦這兩個程序的工作不滿意,您可以嘗試實現add_constructorLoader基礎的替代,在其中您應該能夠重複使用至少map_value

+0

你可能是正確的,這是更適合後處理程序。問題是「foo:object_name」對可以嵌套在任意級別的字典和列表中,所以我不得不遞歸搜索和更新yaml字典。在我已經爲yaml解析的時候重新實現一個遞歸的walker似乎很愚蠢。 – filippo

+0

順便說一句,我從'json'遷移它,通過繼承'JSONDecoder'來獲取我的'__init__'中的lut並提供一個自定義的'object_hook',它更容易。 – filippo

+0

YAML的問題是所有例程都希望數據中的所有狀態都是獨立的。如果您想根據您的環境中的某些本地動態狀態做出解析決定會怎麼樣?我想這不應該發生,因爲解析應該是可重現的。 – filippo