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
一時的なフィールド を持つクラスであるとします。yamlドキュメントにする必要があります。私はこれをしたいと思います:foo
bar
child
child
スニペット #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
理由がわかります。私のコンストラクター関数は再帰用に構築されていません。親オブジェクトの構築を完了する前に子オブジェクトを返す必要があり、子と親が同じオブジェクトである場合は失敗します。
しかし、スニペット #1 が機能するため、明らかに PyYAML にはこの問題を解決するグラフ トラバーサルがあります。おそらく、すべてのオブジェクトを構築するための 1 つのパスと、それらのフィールドに値を設定するための 2 番目のパスがあります。私の質問は、カスタム コンストラクターをこれらのメカニズムにどのように結び付けることができるかということです。
その質問に対する答えは理想的です。しかし、答えがカスタムコンストラクターではこれを行うことができないというものであり、あまり望ましくない代替手段がある場合 (たとえば、YAMLObject
クラスを自分のNode
クラスに混在させるなど)、その答えも高く評価されます。