2017-02-14 3 views
3

Ich habe eine YAML-Datei, die wie folgt aussieht:Objekt kann nicht aus dem Parameter in Konstruktor von PyYAML genannt konstruieren

--- 
!Frog 
    name: tree frog 
    colour: green 
    friends: 
     - !Frog 
      name: motorbike frog 
     - !Frog 
      name: blue arrow frog 

Und ein Python-Programm, das PyYAML verwendet Objekte erstellen nach der Datei:

import yaml 

class Frog(): 
    def __init__(self, name, colour="", friends=None): 
     self.name = name 
     self.colour = colour 
     self.friends = {} 
     if friends != None: 
      for f in friends: 
       self.friends[f.name] = f 
     print("{}'s friends: {}".format(self.name, self.friends)) 

# Constructor for YAML 
def frogConstructor(loader, node) : 
    fields = loader.construct_mapping(node) 
    return Frog(**fields) 

yaml.add_constructor('!Frog', frogConstructor) 

f = open("frog.yaml") 
loaded = yaml.load(f) 

Wie Sie im obigen Code sehen können, ich versuche, einen self.friends Wörterbuch aus dem friends Parameter zu machen (wo der Schlüssel den Namen des Frosches ist und der Wert ist das tatsächliche Frosch-Objekt) an die __init__ Methode. Jedoch führt der Code oben in der folgenden Ausgabe:

tree frog's friends: {} 
motorbike frog's friends: {} 
blue arrow frog's friends: {} 

Wie Sie sehen können, das self.friends Wörterbuch für alle drei der Frösche ist leer, aber der Laubfrosch sollte zwei Freunde haben. Wenn ich einfach self.friends = friends mache, funktioniert es wie erwartet: self.friends ist eine Liste der Freund Frösche. Was mache ich falsch?

Antwort

1

Die Dinge funktionieren, wenn Sie self.friends = friends tun, ist nicht so seltsam. Sie ordnen eine anfänglich leere Liste self.friends zu, eine Liste, die später vom YAML-Parser angehängt wird.

Wenn Sie diese Liste wollen, bevor die Konstruktion Ihrer Frog() gefüllt werden, müssen Sie die deep=True Parameter für construct_mapping() liefern müssen, damit machen dabei sicher, dass die zugrunde liegenden nicht-skalare Konstrukte erste als auch die skalaren diejenigen geschaffen werden.

def frogConstructor(loader, node): 
    fields = loader.construct_mapping(node, deep=True) 
    return Frog(**fields) 

Es gibt jedoch ein paar mehr Probleme mit Ihrem Code (keiner von ihnen die oben Verbot allerdings funktionieren):

  • es nur eine None ist, so ist es besser geeignet ist if friends is not None: zu verwenden als
  • yaml.load ist unsicher, wenn Sie also keine vollständige Kontrolle über Ihre Eingabe haben, könnte das eine abgewischte CD (oder Schlimmeres) bedeuten. PyYAML warnt Sie nicht davor (in meinem ruamel.yaml Parser müssen Sie explizit die unsichere Loader angeben, um eine Warnmeldung zu verhindern).
  • Wenn tree frog Narzisst genug ist, um sich selbst als Freund zu betrachten, oder wenn einer seiner Freunde tree frog einen Freund betrachtet, möchten Sie vielleicht einen Anker und Alias ​​verwenden, um dies anzuzeigen (und nicht nur den gleichen Namen auf einem anderen Frog), und das wird nicht mit dem einfachen Konstruktor funktionieren, den Sie verwenden.
  • frogConstructor, als ein Funktionsname, sollte nicht Kamelfall sein, verwenden Sie stattdessen frog_constructor.

Aufgrund der oben, würde ich nicht verwenden, um die deep=True Parameter, sondern gehe für eine sicherere und vollständigere Lösung durch einen zweistufigen Konstruktor mit:

from ruamel import yaml 

class Frog(): 
    def __init__(self, name): 
     self.name = name 

    def set_values(self, colour="", friends=None): 
     self.colour = colour 
     self.friends = {} 
     if friends is not None: 
      for f in friends: 
       self.friends[f.name] = f 
     print("{}'s friends: {}".format(self.name, self.friends)) 

    def __repr__(self): 
     return "Frog({})".format(self.name) 

# Constructor for YAML 
def frog_constructor(loader, node): 
    fields = loader.construct_mapping(node) 
    frog = Frog(fields.pop('name')) 
    yield frog 
    frog.set_values(**fields) 

yaml.add_constructor('!Frog', frog_constructor, yaml.SafeLoader) 

f = open("frog.yaml") 
loaded = yaml.safe_load(f) 

mit, dass Sie diesen frog.yaml analysieren können:

!Frog &tree_frog 
    name: tree frog 
    colour: green 
    friends: 
     - !Frog 
      name: motorbike frog 
      friends: 
      - *tree_frog 
     - !Frog 
      name: blue arrow frog 

als Ausgabe zu erhalten:

tree frog's friends: {'blue arrow frog': Frog(blue arrow frog), 'motorbike frog': Frog(motorbike frog)} 
motorbike frog's friends: {'tree frog': Frog(tree frog)} 
blue arrow frog's friends: {} 
Verwandte Themen