通过PyYAML序列化namedtuples

python yaml pyyaml namedtuple

1310 观看

2回复

739 作者的声誉

我正在寻找一些合理的方法来使用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表示形式转换为我的构造函数,其余的应该很容易。

作者: Jason 的来源 发布者: 2014 年 7 月 12 日

回应 (2)


1

739 作者的声誉

决定

我能够解决我的问题,虽然方式略逊一筹。

我的应用程序现在使用自己的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 代表在pyyaml中使用相同基类的不同类的实例,以获得解决方案背后的灵感。

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

作者: Jason 发布者: 13.07.2014 10:23

0

2269 作者的声誉

会喜欢不需要重新创建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}

这是如何运作的

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

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

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

作者: bufh 发布者: 16.03.2019 09:56
32x32