Чтобы ruamel.yaml
мог создавать дамп определенного класса, независимо от того, определяете ли вы его, вы получаете его
из стандартной библиотеки или получить, если откуда-то еще, вам нужно зарегистрировать этот класс в представителе.
(Это не обязательно при использовании YAML(typ='unsafe')
, но я предполагаю, что вы не хотите прибегать к этому).
Эта регистрация может быть сделана разными способами. Если у вас есть
сделано yaml = ruamel.yaml.YAML()
или yaml = ruamel.yaml.YAML(typ='safe')
, и хотите представить SomeClass
, вы можете:
- использовать
yaml.register_class(SomeClass)
. Это может работать на других классах в зависимости
о том, как они определены.
- используйте один из декораторов
@yaml_object(yaml)
или @yaml.register_class
, прямо перед class SomeClass:
определение. Это в первую очередь полезно при определении ваших собственных классов
- добавить представителя напрямую, используя:
yaml.representer.add_representer (SomeClass, some_class_to_yaml)
Первые два способа - это просто синтаксический сахар, обернутый вокруг третьего
кстати, и они попытаются использовать метод to_yaml
и атрибут класса
yaml_tag
если доступно, и попробуйте сделать что-нибудь разумное, если
недоступно.
Вы можете попробовать yaml.register(socket.AF_INET)
, но вы заметите, что это не удалось, потому что:
AttributeError: у объекта 'AddressFamily' нет атрибута ' name '
Так что вам придется прибегнуть к третьему способу, используя
add_representer()
. Аргумент some_class_to_yaml
является функцией
это будет
вызывается, когда встречается экземпляр SomeClass
, и эта функция вызывается
с экземпляром yaml.representer
в качестве первого аргумента и с фактическими данными
(экземпляр SomeClass
) в качестве второго аргумента.
Если SomeClass
- это некоторый тип контейнера, который может рекурсивно ссылаться на себя (косвенно),
Вы должны быть особенно внимательны при работе с этой возможностью, но для socket.AF_INET
это не обязательно.
Конкретный тип данных настолько важен, что вам нужно решить,
как представлять тип в YAML. Довольно часто вы увидите, что это
атрибуты SomeClass
используются в качестве ключей в отображении (а затем его
это отображение, которое получает тег), но иногда тип может быть
напрямую представлены в не-коллекции типа, доступных в YAML
в виде строки, int и т. д., для других классов имеет смысл
представлена как (помеченная) последовательность.
Когда вы печатаете type(socket.AF_INET)
, вы заметите, что SomeClass на самом деле AddressFamily
.
И после проверки socket.AF_INET
с использованием dir()
вы заметите, что есть атрибут name
и
это приятно дает вам строку 'AF_INET'
, которая может быть использована, чтобы сказать представителю
как представить эти данные в виде строки, не прибегая к поиску:
import sys
import socket
import ruamel.yaml
def repr_socket(representer, data):
return representer.represent_scalar(u'!socket', data.name)
yaml = ruamel.yaml.YAML()
yaml.representer.add_representer(socket.AddressFamily, repr_socket)
data = dict(sock=socket.AF_INET)
yaml.dump(data, sys.stdout)
, что дает:
sock: !socket AF_INET
Убедитесь, что тег определен как Unicode (необходимо, если вы используете Python 2.7).
Если вы также хотите загрузить это, вы можете расширить constructor
аналогичным образом. Но
на этот раз вы получите Node
, который вам нужно конвертировать в AddressFamily
экземпляр.
yaml_str = """\
- !socket AF_INET
- !socket AF_UNIX
"""
def constr_socket(constructor, node):
return getattr(socket, node.value)
yaml.constructor.add_constructor(u'!socket', constr_socket)
data = yaml.load(yaml_str)
assert data[0] == socket.AF_INET
assert data[1] == socket.AF_UNIX
, который работает без исключения и показывает, что другой
константы в socket
также обрабатываются.