PyYAML ist eine Yaml-Bibliothek für Python.
Eine Geschichte über den Versuch, das Verhalten beim Laden / Dumping von Yaml mithilfe dieser Bibliothek zu ändern.
Versuchen wir, das Verhalten von PyYAML anhand der folgenden beiden Beispiele so zu ändern, dass es sich so verhält, wie Sie es möchten.
--Unterstützt OrderedDict (Eingabe / Ausgabe in einem Format, das von anderen Yaml-Ladern gelesen werden kann)
Dies ist ein Memo, das Sie als Voraussetzung kennen sollten.
--Representer --hook-Objekt zum Hinzufügen von Tags zum Zeitpunkt des Speicherauszugs --Constructor --hook-Objekt zum Generieren eines Python-Ausdrucks beim Ladezeitpunkt
Fügen Sie zur Unterstützung von OrderedDict den folgenden Code hinzu.
def represent_odict(dumper, instance):
return dumper.represent_mapping('tag:yaml.org,2002:map', instance.items())
yaml.add_representer(OrderedDict, represent_odict)
def construct_odict(loader, node):
return OrderedDict(loader.construct_pairs(node))
yaml.add_constructor('tag:yaml.org,2002:map', construct_odict)
Das Einstellungsbeispiel existiert auch in Qiita wie folgt.
Dieses Mal habe ich zufällig einen Blick in PyYAML geworfen, also habe ich beschlossen, Details wie die Bedeutung der obigen Einstellungen zu notieren.
Verwenden Sie "yaml.dump ()", wenn Sie ein Python-Objekt als yaml in einen String konvertieren.
import yaml
with open("ok.yaml", "w") as wf:
yaml.dump({200: "ok"}, wf)
Dieses yaml.dump ()
ruft intern Representer.represent ()
auf.
Dadurch wird das Python-Objekt in ein Node-Objekt in yaml umgewandelt, das mit "Serializer.serialize ()" in eine Zeichenfolge konvertiert wird.
Intern ruft es für jedes Python-Objekt "type ()" auf und verzweigt den Verarbeitungsinhalt entsprechend dem Rückgabetyp.
Wenn kein Verzweigungskandidat vorhanden ist, wird der mro des Objekts verfolgt, um nach einem Kandidaten für die Konvertierung zu suchen.
(Wenn kein Kandidat gefunden wird, wird das Objekt als Endpunkt der Kandidatensuche erreicht und Represent_object ()
aufgerufen.)
Aus Sicht von PyYAML scheint es, dass wir OrderedDict verwenden und richtig diktieren möchten, und wir haben die Darstellungsfunktion für OrderedDict festgelegt.
Representer.add_representer(collections.OrderedDict,
Representer.represent_ordered_dict)
Deshalb sind die folgenden Python-Objekte
d = OrderedDict()
d["a"] = 1
d["z"] = 2
Die Ausgabe wird wie folgt sein.
!!python/object/apply:collections.OrderedDict
- - [a, 1]
- [z, 2]
Um dies zu verhindern, schreiben Sie die Presenter-Einstellungen so, dass die Ausgabe der eines normalen Diktats für "OrderedDict" entspricht. Die Ausgabe ist jedoch erforderlich, damit die Reihenfolge zum Ausgabezeitpunkt beibehalten wird.
Wie bei OrderedDict gibt es eine Representer-Einstellung für dict. Intern wird es als Kartenknoten behandelt.
SafeRepresenter.add_representer(dict,
SafeRepresenter.represent_dict)
class SafeRepresenter:
def represent_dict(self, data):
return self.represent_mapping('tag:yaml.org,2002:map', data)
Aus diesem Grund können Sie Folgendes hinzufügen, um OrderedDict als Kartenknoten auszugeben.
def represent_odict(dumper, instance):
return dumper.represent_mapping('tag:yaml.org,2002:map', instance.items())
yaml.add_representer(OrderedDict, represent_odict)
import yaml
with open("ok.yaml") as rf:
data = yaml.load(rf)
Das gleiche gilt für load, und in yaml.load ()
wird Constructor.get_single_data ()
aufgerufen.
Verwenden Sie erneut "get_single_node ()", um ein Node-Objekt zu erstellen, und verwenden Sie dann "construct_document ()", um es in ein Python-Objekt zu konvertieren.
Auch diesmal können Sie Konvertierungsunterstützung für jeden Knoten hinzufügen. Das Node-Objekt selbst hat die folgende Definition.
class Node(object):
def __init__(self, tag, value, start_mark, end_mark):
self.tag = tag
self.value = value
self.start_mark = start_mark
self.end_mark = end_mark
Dieser Tag-Teil bestimmt, wie verarbeitet werden soll.
Unabhängig davon, ob es sich in der vorherigen Korrespondenz um OrderedDict handelt oder nicht, wird es als Knoten mit einem Map-Tag (als Dikt behandelt) beibehalten. So können Sie die Konvertierung für den Knoten mit dem Tag der Karte festlegen.
Übrigens gibt es tatsächlich Paare in Yaml, was praktisch ist, um die Reihenfolge aufrechtzuerhalten. Es ist einfach, über diese ein bestelltes Diktat zu erstellen.
Diejenigen, die das Tag von Paaren angeben, werden wie folgt konvertiert (Tupel der Länge 2 als Bedeutung des Paares).
s = """\
foo:
!!pairs
- a: b
- x: y
"""
load(s) # {'foo': [('a', 'b'), ('x', 'y')]
Verwenden Sie diese Paare, um die folgenden Einstellungen hinzuzufügen.
def construct_odict(loader, node):
return OrderedDict(loader.construct_pairs(node))
yaml.add_constructor('tag:yaml.org,2002:map', construct_odict)
Das Verwalten der Einstellungsdatei mit json kann problematisch sein, da Sie keine Kommentare schreiben können. In einem solchen Fall kann es in yaml geschrieben sein. Insbesondere ist es schwierig, Einstellungen wie das JSON-Schema und Swagger in JSON zu schreiben, sodass yaml verwendet werden kann.
Meistens verursacht es keine Probleme. Es wird manchmal gesagt, dass der Schlüssel ein numerischer Diktatausdruck ist, weil es einen Unterschied zwischen json und yaml gibt. Insbesondere ist die Situation wie folgt.
Angenommen, es gibt ein solches JSON-Schema.
{
"type": "object",
"patternProperties": {
"\d{3}": {"type": "string"}
},
"additionalProperties": False
}
Eine leichte Unannehmlichkeit tritt auf, wenn das folgende Diktat als ein Wert betrachtet wird, der diesem Schema entspricht (es ist unpraktisch und nicht unangemessen). Dieser Code ist jedoch gültig. Wenn Sie dies mit yaml tun, ist es ungültig.
import json
validate(json.loads(json.dumps({200: "ok"})), schema)
import yaml
from io import StringIO
#Für Yaml, Dumps sowie JSON-Modul,Ich möchte, dass Sie Lasten definieren. ..
def loads(s):
io = StringIO(s)
return yaml.load(io)
def dumps(d):
io = StringIO()
yaml.dump(d, io)
return io.getvalue()
validate(loads(dumps({200: "ok"})), schema) # error
Dies liegt daran, dass die Daten im obigen Diktat auf yaml wie folgt ausgedrückt werden.
200: ok
Das obige Yaml ist ein wenig überflüssig und genau, und es sieht so aus: Selbst wenn der Schlüsseltyp des Kartenknotens auf yaml eine Zahl ist, bleibt er eine Zahl.
!!int 200: ok
#Wenn du so schreibst{'200': ok}Erkannt als
'200': ok
#Oder wenn du so schreibst{'200': ok}Erkannt als
!!str 200: ok
In json ist nur eine Zeichenfolge als Schlüssel des Objekts zulässig, sodass sie automatisch als Zeichenfolge behandelt wird. Die Geschichte des Hinzufügens von Einstellungen, die diesem Verhalten entsprechen. Dies ist wie eine Überprüfung, und Sie können die folgenden Einstellungen hinzufügen.
def construct_json_compatible_map(loader, node):
return {str(k): v for k, v in loader.construct_pairs(node)}
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG # 'tag:yaml.org,2002:map'
yaml.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_json_compatible_map)
Recommended Posts