PyYAML元组的自定义标记

2024-09-27 19:26:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我计划使用PyYAML作为配置文件。一些项目 在这个配置文件中是Python元组的元组。所以,我需要一个 方便的表达方式。可以表示 使用PyYAML的元组如下

print yaml.load("!!python/tuple [ !!python/tuple [1, 2], !!python/tuple [3, 4]]")

但是,对于长序列的 项目。我认为应该可以定义一个自定义标记,比如 python/元组的元组。一、 e.类似的东西

^{pr2}$

请看下面我第一次尝试通过模仿 定义了python/tuple,并尝试进行类似的子类化。它 失败了,但我想我知道我在找什么。我还有时间 尝试这样做是有效的,但这是一个欺骗,因为它只调用eval。在

如果我找不到更好的东西,我就用它。然而,YAML 作为ConfigObj的替代品,ConfigObj使用INI文件,并且 远不如YAML强大,我使用了相同的方法 (即eval)表示元组的元组。所以在这方面不会 更糟。在

一个恰当的解决办法将是最受欢迎的。在

我对我的第一个解决方案有几点意见。在

  1. 我以为构装师 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]]")的结果调用只是

    ((),())
    
  2. 不知道是怎么回事

    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'>

Tags: oforgimportnodeyamlvaluetagtype
1条回答
网友
1楼 · 发布于 2024-09-27 19:26:51

首先从问题2开始:2002年是2002年9月1日在YAML 1.0 draft版本中引入这种标记的年份

问题1更复杂。如果您这样做:

from __future__ import print_function

import yaml

lol = [[1,2], [3,4]]  # list of lists
print(yaml.dump(lol))

你得到(A):

^{pr2}$

但实际上这是(B)的缩写:

!!seq [
  !!seq [
    !!int "1",
    !!int "2",
  ],
  !!seq [
    !!int "3",
    !!int "4",
  ],
]

是(C)的缩写:

!<tag:yaml.org,2002:seq> [
  !<tag:yaml.org,2002:seq> [
    !<tag:yaml.org,2002:int> "1",
    !<tag:yaml.org,2002:int> "2",
  ],
  !<tag:yaml.org,2002:seq> [
    !<tag:yaml.org,2002:int> "3",
    !<tag:yaml.org,2002:int> "4",
  ],
]

A、B和C都加载到列表的原始列表中,因为序列是一个内置类型。在

我不认为扩展yaml的语法(例如()表示元组)是个好主意。要最小化标记,请将示例简化为:

yaml_in = "!tuple [ !tuple [1, 2], !tuple [3, 4]]"

并添加一个构造函数:

yaml.add_constructor("!tuple", construct_tuple)

但这将问题推到创建construct_tuple函数上。一个序列(在constructor.py)中是:

def construct_yaml_seq(self, node):
    data = []
    yield data
    data.extend(self.construct_sequence(node))

但是你不能仅仅用[]替换其中的[],因为通过扩展元组来改变元组是行不通的(使用yield来改变元组的这种两步创建的原因,例如允许在诸如sequence和mapping这样的复杂类型中进行循环引用)。在

您应该定义一个Tuple()类,该类的行为类似于list,直到“locked”(在构造结束时会这样做),从那时起,它的行为应该类似于元组(即不再修改)。下面的方法没有子类化yaml.YAMLObject,因此必须显式地为类提供并注册构造函数和resenter。在

class Tuple(list):

    def _lock(self):
        if hasattr(self, '_is_locked'):
            return
        self._is_locked = True
        self.append = self._append
        self.extend = self._extend

    def _append(self, item):
        raise AttributeError("'Tuple' object has no attribute 'append'")

    def _extend(self, items):
        raise AttributeError("'Tuple' object has no attribute 'extend'")

    def __str__(self):
        return '(' + ', '.join((str(e) for e in self)) + ')'

    # new style class cannot assign something to special method
    def __setitem__(self, key, value):
        if getattr(self, '_is_locked', False):
            raise TypeError("'Tuple' object does not support item assignment")
        list.__setitem__(self, key, value)

    def __delitem__(self, key, value):
        if getattr(self, '_is_locked', False):
            raise TypeError("'Tuple' object does not support item deletion")
        list.__delitem__(self, key, value)

    @staticmethod
    def _construct_tuple(loader, data):
        result = Tuple()
        yield result
        result.extend(loader.construct_sequence(data))
        result._lock()

    @staticmethod
    def _represent_tuple(dumper, node):
        return dumper.represent_sequence("!tuple", node)

# let yaml know how to handle this
yaml.add_constructor("!tuple", Tuple._construct_tuple)
yaml.add_representer(Tuple, Tuple._represent_tuple)

有了它,您可以:

yaml_in = "!tuple [ !tuple [1, 2], !tuple [3, 4]]"
#yaml_in = "!tuple [1, 2]"

data = yaml.load(yaml_in)
print(data)
print(data[1][0])
print(type(data))

获得:

((1, 2), (3, 4))
3
<class '__main__.Tuple'>

这不是一个真正的tuple,但它不允许list类操作。以下活动都会引发相应的错误:

# test appending to the tuple,
try:
    data.append(Tuple([5, 6]))
except AttributeError:
    pass
else:
    raise NotImplementedError
# test extending the tuple,
try:
    data.extend([5, 6])
except AttributeError:
    pass
else:
    raise NotImplementedError
# test replacement of an item
try:
    data[0] = Tuple([5, 6])
except TypeError:
    pass
else:
    raise NotImplementedError
# test deletion of an item
try:
    del data[0]
except TypeError:
    pass
else:
    raise NotImplementedError

最后你可以:

print(yaml.dump(data, default_flow_style=True))

对于以下输出:

!tuple [!tuple [1, 2], !tuple [3, 4]]

如果您真的希望!tuple [[1, 2], [3, 4]]创建一个元组的元组,可以通过在yamlBaseloader类中保持上下文状态,并根据上下文重写从序列到元组或列表构造python对象的方法。这可能必须是一堆上下文状态,以允许嵌套使用!当使用!!seq作为标记时,使用一些显式重写来获取元组中的列表。在


我可能没有检查Tuple()的完整性,只实现了tuple与立即想到的{}相比的限制。
我用PyYAML的增强版本进行了测试:ruamel.yaml,但是这在PyYAML本身中应该是一样的。在

相关问题 更多 >

    热门问题