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.
Essayons de changer le comportement de PyYAML en utilisant les deux exemples suivants comme exemple afin qu'il se comporte comme vous le souhaitez.
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
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.
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)
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 utilisez
construct_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)
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