import sys
from ruamel.yaml import YAML, yaml_object
from ruamel.yaml.compat import StringIO
yaml = YAML()
yaml.default_flow_style = None
data = [('a', 1),
('b', 2),
('c', 3),
('c', 4),
('c', 5),
(4, 1),
('4', 2),
(4, 3),
('4', 4),
]
class MultiDictDuplicateKeyError(Exception):
pass
@yaml_object(yaml)
class MultiDict(dict):
""" no support for deletion of keys """
yaml_tag = '!MultiDict'
def __init__(self, *args, **kw):
self.__duplicate__ = []
if args and not isinstance(args[0], dict):
dict.__init__(self, **kw)
for arg in args[0]:
self[arg[0]] = arg[1]
else:
dict.__init__(self, *args, **kw)
def __setitem__(self, key, value):
if key in self:
if key in self.__duplicate__: # it should already be a list
dict.__getitem__(self, key).append(value)
else:
self.__duplicate__.append(key)
dict.__setitem__(self, key, [dict.__getitem__(self, key), value])
else:
dict.__setitem__(self, key, value)
def __getitem__(self, key):
if key in self.__duplicate__:
raise MultiDictDuplicateKeyError("should use multival(key)")
return dict.__getitem__(self, key)
def multival(self, key):
assert key in self.__duplicate__
values = dict.__getitem__(self, key)
assert isinstance(values, list)
for value in values:
yield value
def __str__(self):
return repr({('__duplicate__' + repr(k) if k in self.__duplicate__ else k): v
for k, v in self.items()})
@classmethod
def to_yaml(cls, representer, node):
return representer.represent_sequence(cls.yaml_tag, [node.__duplicate__, dict(node)])
@classmethod
def from_yaml(cls, constructor, node):
# necessary to deal with recursive data structures
for y in constructor.construct_yaml_map(node.value[1]):
pass
for dup in constructor.construct_yaml_seq(node.value[0]):
pass
ret = cls(y)
ret.__duplicate__ = dup
return ret
md = MultiDict(data)
print('md', md)
print('='*40)
for k in md:
try:
print(repr(k), '->', md[k])
except MultiDictDuplicateKeyError:
for v in md.multival(k):
print(repr(k), '->', v)
print('='*40)
yaml.dump(md, sys.stdout)
print('='*40)
buf = StringIO()
yaml.dump(md, buf)
rt_data = yaml.load(buf.getvalue())
print('rt:', rt_data)
有一些事情还不完全清楚,但假设情况并非如此 只是一个编程练习,我认为你有你从某处读来的数据 有值对,并且您:
在下面,我假设您的输入数据看起来像下面的Python,但是它可以 当然,也可以来自一些数据文件,或者其他一些可以处理重复的结构(Python
dict
s不能):Python中的表示
您表示将数据存储在带有重复键的普通dict中 转换为
__duplicate__c
,这通常不是一个好的选择 因为这会导致问题。在首先,您不能将一个方法添加到处理这些特殊的 键,所以你的代码会在你的程序和/或测试中浮动 专用钥匙被复制。在
其次,这使得表单的原始数据条目:
^{pr2}$不需要额外的处理就无法存储,就像条目一样
,因为在一般情况下,您必须决定如何处理 非字符串重复值,如果将这些值映射到字符串,则会屏蔽 那些“钥匙”。在
即使您的数据仅限于第一个数据项对 作为字符串,最好创建一个
dict
类,或者 子类dict
,这样可以在内部保留 复制钥匙并以特殊方式处理cq。需要特殊的 访问方法。在在YAML
您建议的转储方式将导致无效的YAML。没有其他程序可以简单地加载此数据。 在现代YAML(1.2)中,映射中的键必须是唯一的,否则就是错误。 在已经过时十年的yaml1.1规范中,PyYAML是其中的一部分实现, 您可以忽略第二个键,并发出适当的警告。请注意
使用标记转储数据更合适,例如:
上面显式地需要该标记的处理程序,加载将 如果不处理,则失败。以“YAML”表示作为输入 未修改的PyYAML既不会给出警告错误,也不会加载正确的 值(
3
)。在(PyPI上有一个multidict包,但它不能包含非字符串键。)在
它给出了:
我在这里使用
ruamel.yaml
(免责声明我是该包的作者),但是 经过一些调整(使用add_representer
和add_constructor
),结果相同 应该可以通过使用PyYAML来实现,而不必对BaseConstructor
进行子类化。在相关问题 更多 >
编程相关推荐