Я планирую использовать PyYAML для файла конфигурации. Некоторые элементы в этом файле конфигурации представляют собой кортежи кортежей Python. Итак, мне нужен удобный способ их представления. Кортежи кортежей Python можно представить следующим образом, используя PyYAML
print yaml.load("!!python/tuple [ !!python/tuple [1, 2], !!python/tuple [3, 4]]")
Однако это неудобное обозначение для длинной последовательности элементов. Я думаю, что должна быть возможность определить собственный тег, например python / tuple_of_tuples. Т.е. что-то типа
yaml.load("!!python/tuple_of_tuples [[1,2], [3,4]]")
См. Мою первую попытку определить это ниже, имитируя определение python / tuple и пытаясь создать аналогичные подклассы. Это не удается, но, я думаю, дает представление о том, что мне нужно. У меня есть вторая попытка, которая работает, но это чит, поскольку он просто вызывает eval
.
Если я не найду ничего лучше, я просто воспользуюсь этим. Однако YAML предназначен для замены ConfigObj, который использует файлы INI и значительно менее мощный, чем YAML, и я использовал тот же подход (а именно eval
) для кортежей кортежей. Так что в этом отношении не будет хуже.
Было бы желательно найти правильное решение.
У меня есть пара комментариев по поводу моего первого решения.
Я бы подумал, что конструктор
construct_python_tuple_of_tuples
вернет завершенную структуру, но на самом деле он, кажется, возвращает пустую структуру следующим образом([], [])
Я отследил вызовы, и, похоже, после вызова
construct_python_tuple_of_tuples
происходит много сложных вещей.Возвращаемое значение представляет собой набор списков целых чисел, что очень близко к желаемому результату. Значит, конструкция должна быть завершена позже.
Линия с
tuple([tuple(t) for t in x])
была моей попыткой привести список кортежей к кортежу кортежей, но если я верну его из
construct_python_tuple_of_tuples
, то полученный вызовyaml.load("!!python/tuple_of_tuples [[1,2], [3,4]]")
будет просто((),())
Не уверен, что с
yaml.org,2002
Почему 2002 год?
Первая попытка
import yaml
from yaml.constructor import Constructor
def construct_python_tuple_of_tuples(self, node):
# Complete content of construct_python_tuple
# is
# return tuple(self.construct_sequence(node))
print "node", node
x = tuple(self.construct_sequence(node))
print "x", x
foo = tuple([tuple(t) for t in x])
print "foo", foo
return x
Constructor.construct_python_tuple_of_tuples =
construct_python_tuple_of_tuples
Constructor.add_constructor(
u'tag:yaml.org,2002:python/tuple_of_tuples',
Constructor.construct_python_tuple_of_tuples)
y = yaml.load("!!python/tuple_of_tuples [[1,2], [3,4]]")
print "y", y, type(y)
print y[0], type(y[0])
print y[0][0], type(y[0][0])
Результаты
node SequenceNode(tag=u'tag:yaml.org,2002:python/tuple_of_tuples',
value=[SequenceNode(tag=u'tag:yaml.org,2002:seq',
value=[ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'1'),
ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'2')]),
SequenceNode(tag=u'tag:yaml.org,2002:seq',
value=[ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'3'),
ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'4')])])
x ([], [])
foo ((), ())
y ([1, 2], [3, 4]) <type 'tuple'>
y[0] [1, 2] <type 'list'>
y[0][0] 1 <type 'int'>
Вторая попытка
import yaml
from yaml import YAMLObject, Loader, Dumper
class TupleOfTuples(YAMLObject):
yaml_loader = Loader
yaml_dumper = Dumper
yaml_tag = u'!TupleOfTuples'
#yaml_flow_style = ...
@classmethod
def from_yaml(cls, loader, node):
import ast
print "node", node
print "node.value", node.value, type(node.value)
return ast.literal_eval(node.value)
@classmethod
def to_yaml(cls, dumper, data):
return node
t = yaml.load("!TupleOfTuples ((1, 2), (3, 4))")
print "t", t, type(t)
Результаты следующие:
node ScalarNode(tag=u'!TupleOfTuples', value=u'((1, 2), (3, 4))')
node.value ((1, 2), (3, 4)) <type 'unicode'>
t ((1, 2), (3, 4)) <type 'tuple'>