3

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

一些我不想做的事情:

  • 在命名元组的实例化时,依靠动态调用来添加构造函数/表示器/解析器。这些 YAML 文件可能会在以后存储和重新加载,因此我不能依赖于它们恢复时存在的相同运行时环境。

  • 在全局中注册命名元组。

  • 依赖具有唯一名称的命名元组

我在想一些事情:

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 表示形式放入我的构造函数中,那么其余的应该很容易。

4

2 回答 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 在 pyyaml 中表示具有相同基类的不同类的实例,以获得解决方案背后的灵感。

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

于 2014-07-13T22:23:07.593 回答
0

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

干得好。

TL;博士

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

这是如何运作的

正如您自己发现的那样, anamedtuple没有特殊的类;探索它给出:

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

因此 Python 命名元组的tuple实例是具有附加_asdict()方法的实例。

于 2019-03-16T09:56:38.753 回答