[PYTHON] So ändern Sie das Verhalten beim Laden / Dumping von Yaml mit PyYAML und seinen Details

Einführung

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.

Was ich machen will; was ich vorhabe zu tun

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)

Prämissennotizen

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

Unterstützt OrderedDict (Eingabe / Ausgabe in einem Format, das von anderen Yaml-Ladern gelesen werden kann)

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.

Sprechen Sie über die Ausgabe (yaml.dump)

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)

Sprechen Sie über Eingabe (yaml.load)

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)

Ausgabe in einer mit json kompatiblen Darstellung (setzen Sie den Ziffernschlüssel auf eine Zeichenfolge)

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

So ändern Sie das Verhalten beim Laden / Dumping von Yaml mit PyYAML und seinen Details
Umgang mit Fehlern bei der Installation von Whitenoise und der Bereitstellung auf Heroku
Umgang mit Fehlern bei der Installation von Python und Pip mit Choco
Ich habe zusammengefasst, wie die Boot-Parameter von GRUB und GRUB2 geändert werden
[PostgreSQL] Gewähren der Superuser-Berechtigung, wenn die Anzahl der Benutzer (Rollen) mit Superuser-Berechtigung 0 beträgt.
So ermitteln Sie mit Python den Unterschied zwischen Datum und Uhrzeit in Sekunden
Verhalten bei der Rückkehr in den with-Block
Django ~ Lass es uns im Browser anzeigen ~
Machen Sie ein Thermometer mit Raspberry Pi und machen Sie es im Browser Teil 3 sichtbar
Schaben Sie den Holojour ab und zeigen Sie ihn in der CLI an
So ändern Sie das Verhalten beim Laden / Dumping von Yaml mit PyYAML und seinen Details
Lassen Sie uns die Matrix transponieren und die Matrizen mit numpy multiplizieren.
Lesen Sie die CSV-Datei und zeigen Sie sie im Browser an
POST das Bild mit json und erhalte es mit der Flasche
Extrahieren Sie den Maximalwert mit Pandas und ändern Sie diesen Wert
So machen Sie VS Code auf die venv-Umgebung und ihre Vorteile aufmerksam
So übergeben Sie den Pfad zu der mit pyenv und virtualenv mit PyCharm erstellten Bibliothek
Als ich versuchte, das Root-Passwort mit ansible zu ändern, konnte ich nicht darauf zugreifen.
So zeigen Sie im gesamten Fenster an, wenn Sie das Hintergrundbild mit tkinter einstellen
Umgang mit Fehlern beim Auftreffen auf pip pip
So testen Sie den Friends-of-Friends-Algorithmus mit pyfof
Wie man Kaldi mit JUST Corpus trainiert
Es ist zu einfach, mit rauth auf die Twitter-API zuzugreifen, und ich habe sie ...
So löschen Sie die angegebene Zeichenfolge mit dem Befehl sed! !! !!
[Einführung in Python] Wie iteriere ich mit der Bereichsfunktion?
So erstellen Sie ein Untermenü mit dem Plug-In [Blender]
[Python] So legen Sie den Download-Speicherort mit youtube-dl fest
Freigeben von Ordnern für Docker und Windows mit Tensorflow
Zugriff mit dem Cache beim Lesen von_json mit Pandas
Verwendung des Befehls grep und häufiger Samples
So schleifen und spielen Sie ein GIF-Video mit openCV ab
Wie man Argparse benutzt und den Unterschied zwischen Optparse
[Python] So schreiben Sie den Tabellenstil mit python-pptx um [python-pptx]
Extrahieren Sie den Maximalwert mit Pandas und ändern Sie diesen Wert
[How to!] Lerne und spiele Super Mario mit Tensorflow !!
Umgang mit dem Terminal, das ohne Erlaubnis in die pipenv-Umgebung gelangt, wenn pipenv mit vscode verwendet wird
Wie man einen bestimmten Prozess am Anfang und Ende der Spinne mit Scrapy einfügt