[PYTHON] Comment changer le comportement lors du chargement / vidage de yaml avec PyYAML et ses détails

introduction

PyYAML est une bibliothèque yaml pour python.

Une histoire sur la tentative de changer le comportement lors du chargement / vidage de yaml à l'aide de cette bibliothèque.

Ce que je veux faire

Essayons de changer le comportement de PyYAML en utilisant les deux exemples suivants comme exemple afin qu'il se comporte comme vous le souhaitez.

Notes de prémisse

Ceci est un mémo que vous devriez connaître comme prémisse.

--Representer --hook objet pour ajouter des balises au moment du vidage --Constructor --hook objet pour générer une expression python au moment du chargement

Prend en charge OrderedDict (entrée / sortie dans un format qui peut être lu par d'autres chargeurs yaml)

Pour prendre en charge OrderedDict, ajoutez le code suivant.

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)

L'exemple de réglage existe également dans qiita comme suit.

Cette fois, j'ai jeté un coup d'œil à l'intérieur de PyYAML, alors j'ai décidé de prendre note des détails tels que la signification des paramètres ci-dessus.

Parlez de la sortie (yaml.dump)

Utilisez yaml.dump () lors de la conversion d'un objet Python en une chaîne comme yaml.

import yaml

with open("ok.yaml", "w") as wf:
    yaml.dump({200: "ok"}, wf)

Ce yaml.dump () appelle en interne Representer.represent (). Cela convertit l'objet Python en un objet Node dans yaml, qui est converti en une chaîne avec Serializer.serialize ().

En interne, il appelle type () pour chaque objet Python et branche le contenu de traitement en fonction du type de retour. Si aucun candidat de branche n'existe, le mro de l'objet est tracé pour rechercher un candidat pour la conversion. (Si aucun candidat n'est trouvé, l'objet est atteint comme point final de la recherche de candidat, et represent_object () est appelé.)

Du point de vue de PyYAML, il semble que nous voulions utiliser OrderedDict et dict correctement, et nous avons défini la fonction de représentation pour OrderedDict.

Representer.add_representer(collections.OrderedDict,
        Representer.represent_ordered_dict)

C'est pourquoi les objets Python suivants sont

d = OrderedDict()
d["a"] = 1
d["z"] = 2

La sortie sera la suivante.

!!python/object/apply:collections.OrderedDict
- - [a, 1]
  - [z, 2]

Pour éviter cela, réécrivez les paramètres du présentateur de sorte que ʻOrderedDict` produise la même chose qu'un dict normal. Cependant, il est nécessaire de sortir pour que l'ordre soit maintenu à la synchronisation de sortie.

Comme avec OrderedDict, il existe un paramètre Representer pour dict. En interne, il est traité comme un nœud de carte.

SafeRepresenter.add_representer(dict,
        SafeRepresenter.represent_dict)

class SafeRepresenter:
    def represent_dict(self, data):
        return self.represent_mapping('tag:yaml.org,2002:map', data)

C'est pourquoi vous pouvez ajouter ce qui suit pour générer OrderedDict en tant que nœud de carte.

def represent_odict(dumper, instance):
    return dumper.represent_mapping('tag:yaml.org,2002:map', instance.items())

yaml.add_representer(OrderedDict, represent_odict)

Parlez d'entrée (yaml.load)

import yaml

with open("ok.yaml") as rf:
    data = yaml.load(rf)

La même chose est vraie pour load, et à l'intérieur de yaml.load (), Constructor.get_single_data () ʻest appelé. Encore une fois, utilisez get_single_node ()pour créer un objet Node, puis utilisezconstruct_document ()` pour le convertir en un objet Python.

Cette fois également, vous pouvez ajouter la prise en charge de la conversion pour chaque nœud. L'objet Node lui-même a la définition suivante.

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

Cette partie d'étiquette détermine comment traiter.

Qu'il s'agisse de OrderedDict ou non dans la correspondance précédente, il sera conservé comme un nœud avec une balise de map (traité comme un dict). Ainsi, vous pouvez définir la conversion du nœud avec la balise de la carte.

Au fait, il existe en fait des paires en yaml, ce qui est pratique pour garder l'ordre. Il est facile de faire un ordre ordonné via ceci.

Ceux qui spécifient l'étiquette de paires sont convertis comme suit (tuple de longueur 2 comme signification de paire).

s = """\
foo:
  !!pairs
  - a: b
  - x: y
"""
load(s)  # {'foo': [('a', 'b'), ('x', 'y')]

Utilisez ces paires pour ajouter les paramètres suivants.

def construct_odict(loader, node):
    return OrderedDict(loader.construct_pairs(node))

yaml.add_constructor('tag:yaml.org,2002:map', construct_odict)

Sortie dans une représentation compatible avec json (définir la clé numérique dict sur une chaîne de caractères)

La gestion du fichier de paramètres avec json peut être gênante car vous ne pouvez pas écrire de commentaires. Dans un tel cas, il peut être écrit en yaml. En particulier, il est difficile d'écrire des paramètres tels que le schéma json et swagger dans json, donc yaml peut être utilisé.

La plupart du temps, cela ne pose aucun problème. On dit parfois que key est une expression numérique dict parce qu'il y a une différence entre json et yaml. Plus précisément, la situation est la suivante.

Supposons qu'il existe un tel schéma json.

{
    "type": "object",
    "patternProperties": {
        "\d{3}": {"type": "string"}
    },
    "additionalProperties": False
}

Un léger inconvénient se produit lorsque l'on considère le dict suivant comme une valeur qui correspond à ce schéma (c'est peu pratique et non inapproprié). Ce code est cependant valide. Si vous faites cela avec yaml, ce sera invalide.

import json

validate(json.loads(json.dumps({200: "ok"})), schema)
import yaml
from io import StringIO

#Pour yaml, dumps ainsi que le module json,Je veux que vous définissiez les charges. ..

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

C'est parce que les données dans le dict ci-dessus sont exprimées comme suit sur yaml.

200: ok

Le yaml ci-dessus est un peu redondant et précis, et il ressemble à ceci: Sur yaml, même si le type clé de la carte Node est un nombre, il reste un nombre.

!!int 200: ok

#Si tu écris comme ça{'200': ok}Reconnu comme
'200': ok

#Ou si tu écris comme ça{'200': ok}Reconnu comme
!!str 200: ok

Sur json, seule une chaîne de caractères est autorisée comme clé de l'objet, elle est donc automatiquement traitée comme une chaîne de caractères. L'histoire de l'ajout de paramètres correspondant à ce comportement. C'est comme un examen jusqu'à présent, et vous pouvez ajouter les paramètres suivants.

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

Comment changer le comportement lors du chargement / vidage de yaml avec PyYAML et ses détails
Comment gérer les erreurs lors de l'installation de whitenoise et du déploiement sur Heroku
Comment gérer les erreurs lors de l'installation de Python et de pip avec choco
J'ai résumé comment changer les paramètres de démarrage de GRUB et GRUB2
[PostgreSQL] Comment accorder une autorité de superutilisateur lorsque le nombre d'utilisateurs (rôles) avec l'autorité de superutilisateur est de 0.
Comment obtenir la différence de date et d'heure en secondes avec Python
Comportement lors du retour dans le bloc with
Django ~ Affichons-le sur le navigateur ~
Fabriquez un thermomètre avec Raspberry Pi et rendez-le visible sur le navigateur Partie 3
Gratter l'holojour et l'afficher dans la CLI
Comment changer le comportement lors du chargement / vidage de yaml avec PyYAML et ses détails
Transposons la matrice et multiplions les matrices par numpy.
Lisez le fichier csv et affichez-le dans le navigateur
POSTER l'image avec json et la recevoir avec flask
Extraire la valeur maximale avec les pandas et modifier cette valeur
Comment sensibiliser VS Code à l'environnement venv et à ses avantages
Comment passer le chemin vers la bibliothèque construite avec pyenv et virtualenv avec PyCharm
Lorsque j'ai essayé de changer le mot de passe root avec ansible, je ne pouvais pas y accéder.
Comment afficher dans toute la fenêtre lors de la définition de l'image d'arrière-plan avec tkinter
Comment gérer les erreurs en frappant pip ②
Comment essayer l'algorithme des amis d'amis avec pyfof
Comment entraîner Kaldi avec JUST Corpus
C'est trop facile d'accéder à l'API Twitter avec rauth et je l'ai ...
Comment supprimer la chaîne de caractères spécifiée avec la commande sed! !! !!
[Introduction à Python] Comment itérer avec la fonction range?
Comment créer un sous-menu avec le plug-in [Blender]
[Python] Comment spécifier l'emplacement de téléchargement avec youtube-dl
Comment partager des dossiers avec Docker et Windows avec tensorflow
Comment accéder avec cache lors de la lecture_json avec pandas
Comment utiliser la commande grep et des exemples fréquents
Comment boucler et lire une vidéo gif avec openCV
Comment utiliser argparse et la différence entre optparse
[Python] Comment réécrire le style de table avec python-pptx [python-pptx]
Extraire la valeur maximale avec les pandas et modifier cette valeur
[Comment!] Apprenez et jouez à Super Mario avec Tensorflow !!
Comment gérer l'entrée du terminal dans l'environnement pipenv sans autorisation lors de l'utilisation de pipenv avec vscode
Comment insérer un processus spécifique au début et à la fin de l'araignée avec la tremblante