2016-04-30 14 views
0

Ich habe mit PyYAML docs zu kämpfen, um eine wahrscheinlich einfache Sache zu verstehen. ein Wörterbuch, die String-Namen Python Objekte abbildet:pyyaml ​​map dict zu dict von objekten

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

und ich möchte eine YAML-Datei wie folgt laden und alle „foo“ Knoten in meine Dictionary-Objekte (die inverse Karte, Dumping, ist nicht wirklich notwendig)

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

Mein erster Gedanke war add_constructor zu verwenden, aber ich konnte nicht einen Weg finden, ihm eine zusätzliche kwarg zu geben. Vielleicht ein benutzerdefinierter Lader? PyYAML docs sind nicht wirklich hilfreich oder wahrscheinlich suche ich die falschen Keywords ...

konnte ich wie

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

Aber Erkennung nur foo Knoten unter Verwendung eines benutzerdefinierten Tags akzeptieren wäre schöner

Antwort

2

Sie suchen nicht nach den falschen Schlüsselwörtern, dies ist nichts, was irgendeinen der YAML-Parser, von denen ich weiß, gemacht wurde. YAML-Parser laden eine, möglicherweise komplexe, Datenstruktur, die in sich geschlossen ist. Und was Sie tun möchten, ist, diese eigenständige Struktur während eines der Parsing-Schritte in eine bereits existierende Struktur (lut) zusammenzuführen. Der Parser erstellt wird Zwicken zu ermöglichen, indem alternative Routinen bietet nicht durch Routinen Bereitstellung + Daten

Es gibt keine Möglichkeit, dass für in PyYAML gebaut, dh es gibt keine integrierte Möglichkeit, den Lader über lut zu sagen, dass Machen Sie PyYAML etwas damit zu tun, und sicherlich nicht, um Schlüssel-Wert-Paare (vorausgesetzt, dass Sie das mit den Knoten meinen) als Werte zu seinen Schlüsseln.

Wahrscheinlich der einfachste Weg zu erhalten, was Sie wollen, ist mit einem Post-Prozess, der lut und die Daten aus Ihrer YAML-Datei geladen (die auch ein Diktat) und kombinieren die beiden.

Wenn Sie versuchen wollen und tun dies mit add_constructor, dann, was Sie tun müssen, ist eine Klasse mit einer __call__ Verfahren zu konstruieren, eine Instanz der Klasse mit lut als Argument erstellen und als diese Instanz in als Alternative Konstruktor): yaml.org 2002:

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) 

In dem Du 'your_tag' mit u'tag ersetzen Karte‘, wenn Sie Konstruktor wollen (alle) normale dicts zu handhaben.

Eine andere Möglichkeit besteht darin, dies während des YAML-Ladens zu tun, aber Sie können nicht nur die Loader oder eine ihrer Komponenten (Constructor) optimieren, da Sie normalerweise die Klasse nicht als Objekt übergeben. Sie benötigen ein Objekt, um lut anhängen zu können. Also, was Sie tun würden, ist Ihren eigenen Konstruktor und Ihren eigenen Lader zu erstellen, der diesen Konstruktor und dann eine load() Ersetzung verwendet, die Ihren Lader instanziiert, fügt lut an (indem Sie es einfach als ein eindeutiges Attribut hinzufügen oder indem Sie es als Parameter übergeben und Übergabe an Ihren Konstrukteur).

Ihr Konstruktor, der eine Unterklasse von einem der vorhandenen Konstruktoren sein sollte, muss dann construct_mapping() haben, der zuerst die Elternklasse 'construct_mapping() aufruft und vor dem Zurückgeben des Ergebnisses prüft, ob er dieses Attribut aktualisieren kann lut wurde zugewiesen.Sie können dies nicht basierend auf die Tasten des dict für foo tun, denn wenn Sie einen solchen Schlüssel haben Sie keinen Zugriff auf den übergeordneten Knoten, den Sie lut zuweisen müssen. Was Sie tun müssen, ist zu sehen, ob einer der Werte des Mappings ein dict ist, das einen Schlüsselnamen foo hat, und wenn dies der Fall ist, kann das Wörterbuch verwendet werden, um lut basierend auf dem Wert zu foo zu aktualisieren. sicher implementieren zunächst die Postprozessstufe mit zwei Routinen

Ich würde:

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} 

Ich bin nicht sicher, was Sie wirklich meinen, mit „Zuweisen alle foo Knoten“, die YAML Daten hat keine Knoten auf der obersten Ebene Es hat nur Schlüssel und Werte. Sie weisen also entweder dieses Paar oder nur seinen Wert zu (ein Diktat).

Sobald diese beiden Routinen zufriedenstellend arbeiten, können Sie versuchen, die add_constructor oder Loader Alternativen zu implementieren, in denen sollten Sie in der Lage sein, wieder zu verwenden, zumindest map_value

+0

Du hast wahrscheinlich recht, dass dies besser geeignet für eine Nachverarbeitungsroutine. Das Problem ist, dass "foo: object_name" -Paare in beliebigen Ebenen von Dicts und Listen verschachtelt sein könnten, so dass ich das yaml-dict rekursiv suchen und aktualisieren müsste. Es schien dumm zu sein, einen rekursiven Walker neu zu implementieren, während ich es bereits für das YAML-Parsing mache. – filippo

+0

Übrigens migriere ich dies von 'json', wo es einfacher war,' JSONDecoder' abzuleiten, um mein lut in '__init__' zu erhalten und einen eigenen' object_hook' zu erstellen. – filippo

+0

Das Problem mit YAML ist, dass alle Routinen erwarten, dass der gesamte Status in den Daten enthalten ist. Was ist, wenn Sie Parsing-Entscheidungen basierend auf einem lokalen dynamischen Status aus Ihrer Umgebung treffen möchten? Ich vermute, das sollte nicht passieren, da das Parsen reproduzierbar sein sollte. – filippo