代码之家  ›  专栏  ›  技术社区  ›  Rene Knuvers

在Python中使用YAML或JSON序列化RangeDict

  •  2
  • Rene Knuvers  · 技术社区  · 7 年前

    我正在使用 RangeDict 制作包含范围的词典。当我使用Pickle时,它很容易写入文件,然后再读取。

    import pickle
    from rangedict import RangeDict
    
    rngdct = RangeDict()
    rngdct[(1, 9)] = \
        {"Type": "A", "Series": "1"}
    rngdct[(10, 19)] = \
        {"Type": "B", "Series": "1"}
    
    with open('rangedict.pickle', 'wb') as f:
        pickle.dump(rngdct, f)
    

    基本上,将代码更改为调用yaml并在中打开文件 'w' 模式,不在 'wb' 在书写端执行此操作,但当我在另一个脚本中读取文件时,会出现以下错误:

    File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/yaml/constructor.py", line 129, in construct_mapping
    value = self.construct_object(value_node, deep=deep)
    File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/yaml/constructor.py", line 61, in construct_object
    "found unconstructable recursive node", node.start_mark)
    yaml.constructor.ConstructorError: found unconstructable recursive node
    

    我在这里迷路了。如何序列化rangedict对象并以其原始形式读回?

    1 回复  |  直到 7 年前
        1
  •  0
  •   Anthon    7 年前

    TL;博士;跳转到此答案的底部查看工作代码


    我肯定有些人讨厌 pickle 在重构代码时(当pickle对象的类移动到不同的文件时),它肯定会带来一些麻烦。但更大的问题是泡菜是不安全的,只是YAML妨碍了你使用它。

    有趣的是,您不能将pickle设置为更可读的 protocol level 0

    泡菜。转储(rngdct,f,协议=0) 将抛出:

    插槽 不能腌制

    这是因为 RangeDict 模块/类有点简约,这也表明(或者更确切地说不是)如果你试图做:

    print(rngdict)
    

    只需打印 {}

    dump() load() ). 尽管这可能会转储通用Python类,但您必须意识到这是在Python 3.0之前实现的,或者与Python 3.0大致同时实现的。(稍后实现了对Python 3的支持)。尽管YAML解析器没有理由转储和加载 是的,它不会与 泡菜

    无论如何,没有特定的重新输入(和构造函数) RangeDict公司 对象,使用YAML真的没有任何意义:它使得加载可能不安全,并且您的YAML包括 这使得该对象高效。如果你这样做了 yaml.dump()

    !!python/object:rangedict.RangeDict
    _root: &id001 !!python/object/new:rangedict.Node
      state: !!python/tuple
      - null
      - color: 0
        left: null
        parent: null
        r: !!python/tuple [1, 9]
        right: !!python/object/new:rangedict.Node
          state: !!python/tuple
          - null
          - color: 1
            left: null
            parent: *id001
            r: !!python/tuple [10, 19]
            right: null
            value: {Series: '1', Type: B}
        value: {Series: '1', Type: A}
    

    可读 在YAML中的代表将是:

    !rangedict
    [1, 9]:
      Type: A
      Series: '1'
    [10, 19]:
      Type: B
      Series: '1'
    

    ruamel.yaml RangeDict公司 要提供合适的重新输入和构造函数(类)方法:

    import io
    import ruamel.yaml
    from rangedict import RangeDict
    
    class MyRangeDict(RangeDict):
        yaml_tag = u'!rangedict'
    
        def _walk(self, cur):
            # walk tree left -> parent -> right
            if cur.left:
                for x in self._walk(cur.left):
                    yield x
            yield cur.r
            if cur.right:
                for x in self._walk(cur.right):
                    yield x
    
        @classmethod
        def to_yaml(cls, representer, node):
            d = ruamel.yaml.comments.CommentedMap()
            for x in node._walk(node._root):
                d[ruamel.yaml.comments.CommentedKeySeq(x)] = node[x[0]]
            return representer.represent_mapping(cls.yaml_tag, d)
    
        @classmethod
        def from_yaml(cls, constructor, node):
            d = cls()
            for x, y in node.value:
                x = constructor.construct_object(x, deep=True)
                y = constructor.construct_object(y, deep=True)
                d[x] = y
            return d
    
    
    rngdct = MyRangeDict()
    rngdct[(1, 9)] = \
        {"Type": "A", "Series": "1"}
    rngdct[(10, 19)] = \
        {"Type": "B", "Series": "1"}
    
    yaml = ruamel.yaml.YAML()
    yaml.register_class(MyRangeDict)  # tell the yaml instance about this class
    
    buf = io.StringIO()
    
    yaml.dump(rngdct, buf)
    data = yaml.load(buf.getvalue())
    
    # test for round-trip equivalence:
    for x in data._walk(data._root):
        for y in range(x[0], x[1]+1):
            assert data[y]['Type'] == rngdct[y]['Type']
            assert data[y]['Series'] == rngdct[y]['Series']
    

    buf.getvalue() 正是前面显示的可读表示。

    如果你不得不面对倾销 本身(即,由于您使用的某些库具有 硬编码),然后可以添加 MyRangeDict RangeDict公司 通过嫁接/修补。

    推荐文章