通过PyYAML序列化namedtuples

问题描述 投票:2回答:2

我正在寻找一些合理的方法来使用PyYAML在YAML中序列化namedtuples。

我不想做的一些事情:

  • 依赖于动态调用,在实例化namedtuple时添加构造函数/表示符/解析器。这些YAML文件可以在以后存储和重新加载,因此我不能依赖它们在恢复时存在的相同运行时环境。
  • 在global中注册namedtuples。
  • 依靠具有唯一名称的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表示形式转换为我的构造函数,其余的应该很容易。

python yaml pyyaml namedtuple
2个回答
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函数的想法,但这实现了我的目标。


0
投票

会喜欢一个不需要重新创建namedtuple功能的想法,但这实现了我的目标。

干得好。

TL;DR

使用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}

How does this work

正如你自己发现的那样,namedtuple没有特殊的课程;探索它给出:

>>> collections.namedtuple('Bar', '').mro()
[<class '__main__.Bar'>, <type 'tuple'>, <type 'object'>]

因此,名为元组的Python实例是带有额外tuple方法的_asdict()实例。

© www.soinside.com 2019 - 2024. All rights reserved.