问题描述
我正在寻找一些合理的方法来使用 PyYAML 序列化 YAML 中的命名元组.
I'm looking for some reasonable way to serialize namedtuples in YAML using PyYAML.
一些我不想做的事情:
依靠动态调用在命名元组的实例化时添加构造函数/表示器/解析器.这些 YAML 文件可能会在以后存储和重新加载,因此我不能依赖它们恢复时存在的相同运行时环境.
Rely on a dynamic call to add a constructor/representor/resolver upon instantiation of the namedtuple. These YAML files may be stored and re-loaded later, so I cannot rely on the same runtime environment existing when they are restored.
在全局注册命名元组.
依赖具有唯一名称的命名元组
Rely on the namedtuples having unique names
我正在考虑以下几点:
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 路径下的应用程序模块中
Assuming this was in an application module at the path myapp/util.py
但是,当我尝试加载时,我没有进入构造函数:
I'm not getting into the constructor, however, when I try to load:
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.
It will fail to find New in myapp.util.
我也尝试了多种其他方法,这只是我认为可能效果最好的一种.
I tried a variety of other approaches as well, this was just one that I thought might work best.
免责声明:即使我进入了正确的构造函数,我也知道我的规范还需要进一步研究哪些参数是如何保存的,它们是如何传递到结果对象中的,但对我来说,第一步是将 YAML 表示放入我的构造函数,那么剩下的应该很容易了.
Disclaimer: Even once I get into the proper constructor I'm aware my spec will need further work regarding what arguments get saved how they are passed into the resulting object, but the first step for me is to get the YAML representation into my constructor function, then the rest should be easy.
推荐答案
我能够解决我的问题,尽管方式略显不太理想.
I was able to solve my problem, though in a slightly less than ideal way.
我的应用程序现在使用它自己的namedtuple
实现;我复制了 collections.namedtuple
源代码,为所有要继承的新 namedtuple
类型创建了一个基类,并修改了模板(为了简洁起见,下面摘录,只是突出显示了从命名元组来源).
My application now uses its own namedtuple
implementation; I copied the collections.namedtuple
source, created a base class for all new namedtuple
types to inherit, and modified the template (excerpts below for brevity, simply highlighting whats change from the namedtuple source).
class namedtupleBase(tuple):
pass
_class_template = '''\
class {typename}(namedtupleBase):
'{typename}({arg_list})'
对 namedtuple 函数本身做一点改动,将新类添加到命名空间中:
One little change to the namedtuple function itself to add the new class into the namespace:
namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
OrderedDict=OrderedDict, _property=property, _tuple=tuple,
namedtupleBase=namedtupleBase)
现在注册一个multi_representer
解决了这个问题:
Now registering a multi_representer
solves the problem:
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)
提示 在pyyaml中用相同的基类表示不同类的实例以获取解决方案背后的灵感.
Hattip to Represent instance of different classes with the same base class in pyyaml for the inspiration behind the solution.
想要一个不需要重新创建 namedtuple 函数的想法,但这实现了我的目标.
Would love an idea that doesn't require re-creating the namedtuple function, but this accomplished my goals.
这篇关于通过 PyYAML 序列化命名元组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!