[英]How do I handle recursion in a custom PyYAML constructor?
PyYAML 可以處理常規 Python 對象中的循環圖。 例如:
片段 #1。
class Node: pass
a = Node()
b = Node()
a.child = b
b.child = a
# We now have the cycle a->b->a
serialized_object = yaml.dump(a)
object = yaml.load(serialized_object)
這段代碼成功了,很明顯有一些機制可以在加載序列化對象時防止無限遞歸。 當我編寫自己的 YAML 構造函數時如何利用它?
例如,假設Node
是一個具有瞬態字段foo
和bar
以及非瞬態字段child
。 只有child
應該將其放入 yaml 文檔中。 我希望這樣做:
片段#2。
def representer(dumper, node):
return dumper.represent_mapping("!node", {"child": node.child})
def constructor(loader, data):
result = Node()
mapping = loader.construct_mapping(data)
result.child = mapping["child"]
return result
yaml.add_representer(Node, representer)
yaml.add_constructor("!node", constructor)
# Retry object cycle a->b->a from earlier code snippet
serialized_object = yaml.dump(a)
print serialized_object
object = yaml.load(serialized_object)
但它失敗了:
&id001 !node
child: !node
child: *id001
yaml.constructor.ConstructorError: found unconstructable recursive node:
in "<string>", line 1, column 1:
&id001 !node
我明白為什么了。 我的構造函數不是為遞歸而構建的。 它需要在完成構造父對象之前返回子對象,並且當子對象和父對象是同一個對象時會失敗。
但很明顯 PyYAML 具有解決這個問題的圖遍歷,因為 Snippet #1 有效。 也許有一次構建所有對象,第二次填充它們的字段。 我的問題是,我的自定義構造函數如何與這些機制相關聯?
這個問題的答案將是理想的。 但是如果答案是我不能用自定義構造函數來做到這一點,並且有一個不太理想的替代方案(例如將YAMLObject
類混合到我的Node
類中),那么這個答案也將不勝感激。
對於可能涉及遞歸(映射/字典、序列/列表、對象)的復雜類型,構造函數無法一次性創建對象。 因此,應yield
在所構造的對象constructor()
函數,然后更新后that¹任何值:
def constructor(loader, data):
result = Node()
yield result
mapping = loader.construct_mapping(data)
result.child = mapping["child"]
這擺脫了錯誤。
¹我不認為這在任何地方都有記錄,如果我沒有仔細查看py/constructor.py
,同時將 PyYAML 升級到ruamel.yaml ,我就不會知道如何做到這一點。 典型案例:閱讀Luke源碼
我對 PyYaml 的第一印象是它試圖將某種程度的接口/行為保持為 JSON(轉儲/加載)。
我學習並欣賞 JSON 功能,因為我很容易將 JSON 讀入動態構造的類型。 然而,我對 JSON 格式本身存在問題,尤其是缺乏對多行字符串、注釋和可讀性的支持。
使用 PyYAML 我發現將 yaml 反序列化為類型非常困難。 似乎有很多我沒有時間/興趣學習的箍要跳過。 考慮以下將 JSON 反序列化為類型的代碼:
with open(file) as filereader: json.load(filereader, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
通過使用對象加載鈎子,我可以將字典轉換為命名元組。 現在 pyyaml 非常擅長將 yaml 轉換為字典。 我最終應用了這個 hack,我從 yamlfile -> 字典 -> json 字符串 -> 對象,如下所示:
json.loads(json.dumps(yaml.load(filereader)), object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
這一行通過中間 json 轉換將 yaml 文件讀入類型化對象。 在我的情況下,這是一個值得的黑客,因為替代方案要復雜得多。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.