我正在寻找一些使用PyYAML在YAML中序列化namedtuples的合理方法。
我不想做的一些事情:
依靠动态调用在实例化namedtuple时添加构造函数/表示符/解析器。这些YAML文件可以在以后存储和重新加载,因此我不能依赖它们在恢复时存在的相同运行时环境。
在全局注册namedtuples。
依靠具有唯一名称的命名元组
我正在考虑这些问题:
class namedtuple(object):
def __new__(cls, *args, **kwargs):
x = collections.namedtuple(*args, **kwargs)
class New(x):
def __getstate__(self):
return {
"name": self.__class__.__name__,
"_fields": self._fields,
"values": self._asdict().values()
}
return New
def namedtuple_constructor(loader, node):
import IPython; IPython.embed()
value = loader.construct_scalar(node)
import re
pattern = re.compile(r'!!python/object/new:myapp.util\.')
yaml.add_implicit_resolver(u'!!myapp.util.namedtuple', pattern)
yaml.add_constructor(u'!!myapp.util.namedtuple', namedtuple_constructor)
假设这是在路径myapp / util.py
的应用程序模块中但是,当我尝试加载时,我没有进入构造函数:
from myapp.util import namedtuple
x = namedtuple('test', ['a', 'b'])
t = x(1,2)
dump = yaml.dump(t)
load = yaml.load(dump)
无法在myapp.util中找到New。
我尝试了其他各种方法,这只是我认为可能效果最好的方法之一。
免责声明:即使我进入正确的构造函数,我也知道我的规范需要进一步的工作,关于哪些参数被保存如何传递给结果对象,但我的第一步是将YAML表示转换为我的构造函数,其余应该很容易。
答案 0 :(得分:1)
我能够解决我的问题,但方式略逊于理想。
我的应用程序现在使用自己的namedtuple
实现;我复制了collections.namedtuple
源代码,为所有要继承的新namedtuple
类型创建了一个基类,并修改了模板(为简洁起见,下面摘录,只是突出显示了来自namedtuple源的更改)。
class namedtupleBase(tuple):
pass
_class_template = '''\
class {typename}(namedtupleBase):
'{typename}({arg_list})'
对namedtuple函数本身进行一点改动,将新类添加到命名空间中:
namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
OrderedDict=OrderedDict, _property=property, _tuple=tuple,
namedtupleBase=namedtupleBase)
现在注册multi_representer
可以解决问题:
def repr_namedtuples(dumper, data):
return dumper.represent_mapping(u"!namedtupleBase", {
"__name__": data.__class__.__name__,
"__dict__": collections.OrderedDict(
[(k, v) for k, v in data._asdict().items()])
})
def consruct_namedtuples(loader, node):
value = loader.construct_mapping(node)
cls_ = namedtuple(value['__name__'], value['__dict__'].keys())
return cls_(*value['__dict__'].values())
yaml.add_multi_representer(namedtupleBase, repr_namedtuples)
yaml.add_constructor("!namedtupleBase", consruct_namedtuples)
Hattip向Represent instance of different classes with the same base class in pyyaml寻求解决方案的灵感。
会喜欢一个不需要重新创建namedtuple函数的想法,但这完成了我的目标。
答案 1 :(得分:0)
会喜欢一个不需要重新创建
namedtuple
函数的想法,但这可以实现我的目标。
你在这里。
使用PyAML 3.12进行概念验证。
import yaml
def named_tuple(self, data):
if hasattr(data, '_asdict'):
return self.represent_dict(data._asdict())
return self.represent_list(data)
yaml.SafeDumper.yaml_multi_representers[tuple] = named_tuple
注意:要保持清洁,您应该像对待处理一样使用add_multi_representer()
方法之一,并使用自定义表示器/加载器。
这给您:
>>> import collections
>>> Foo = collections.namedtuple('Foo', 'x y z')
>>> yaml.safe_dump({'foo': Foo(1,2,3), 'bar':(4,5,6)})
'bar: [4, 5, 6]\nfoo: {x: 1, y: 2, z: 3}\n'
>>> print yaml.safe_dump({'foo': Foo(1,2,3), 'bar':(4,5,6)})
bar: [4, 5, 6]
foo: {x: 1, y: 2, z: 3}
您自己发现,namedtuple
没有特殊的类;探索它会给出:
>>> collections.namedtuple('Bar', '').mro()
[<class '__main__.Bar'>, <type 'tuple'>, <type 'object'>]
因此,名为元组的Python实例是带有附加tuple
方法的_asdict()
实例。