【问题标题】:Formatting PyYAML dump() output格式化 PyYAML 转储()输出
【发布时间】:2012-12-23 03:04:34
【问题描述】:

我有一个字典列表,我想对其进行序列化:

list_of_dicts = [ { 'key_1': 'value_a', 'key_2': 'value_b'},
                  { 'key_1': 'value_c', 'key_2': 'value_d'},
                  ...
                  { 'key_1': 'value_x', 'key_2': 'value_y'}  ]

yaml.dump(list_of_dicts, file, default_flow_style = False)

产生以下内容:

- key_1: value_a
  key_2: value_b
- key_1: value_c
  key_2: value_d
(...)
- key_1: value_x
  key_2: value_y

但我想得到这个:

- key_1: value_a
  key_2: value_b
                     <-|
- key_1: value_c       | 
  key_2: value_d       |  empty lines between blocks
(...)                  |
                     <-|
- key_1: value_x
  key_2: value_y

PyYAML documentation 非常简短地讨论了 dump() 参数,似乎没有关于这个特定主题的任何内容。

手动编辑文件以添加换行符大大提高了可读性,之后结构仍然可以正常加载,但我不知道如何使转储方法生成它。

一般来说,除了简单的缩进之外,还有什么方法可以更好地控制输出格式?

【问题讨论】:

    标签: python yaml pyyaml


    【解决方案1】:

    使用库没有简单的方法来做到这一点(yaml dumper 语法树中的节点对象是被动的,不能发出这个信息),所以我最终得到了

    stream = yaml.dump(list_of_dicts, default_flow_style = False)
    file.write(stream.replace('\n- ', '\n\n- '))
    

    【讨论】:

    • 谢谢!必须使用类似的东西来格式化列表。 PyYAML 不会在- 之前放置缩进,而我们使用的 YAML 消费库预计会有一些缩进。所以我们不得不做replace('- ', ' - ')
    • 该节点是被动的是真实的但无关紧要,因为节点也不会发出任何其他信息(即ScalarNodes 不会发出自己的值)。 Emitter 确实获得了一个节点的值(在适当的时候)并发出它,如果你在节点上附加额外的信息,并增强相关的 Emitter 方法来处理这些额外的信息(就像我在 ruamel.yaml 中所做的那样),那么就会有绝对不需要做那种基于字符串的粗略的后处理。
    • @Andrei 使用ruamel.yaml,您可以设置yaml.indent(sequence=3, offset=1) 并在不进行后处理的情况下获得该输出。
    【解决方案2】:

    虽然有点笨拙,但我的目标与 OP 相同。 我通过子类化 yaml.Dumper 解决了它

    from yaml import Dumper
    
    class MyDumper(Dumper):
    
      def write_indent(self):
        indent = self.indent or 0
        if not self.indention or self.column > indent \
            or (self.column == indent and not self.whitespace):
          self.write_line_break()
    
    
        ##########$#######################################
        # On the first level of indentation, add an extra
        # newline
    
        if indent == 2:
          self.write_line_break()
    
        ##################################################
    
        if self.column < indent:
          self.whitespace = True
          data = u' '*(indent-self.column)
          self.column = indent
          if self.encoding:
            data = data.encode(self.encoding)
          self.stream.write(data)
    

    你这样称呼它:

    print dump(python_dict, default_flow_style=False, width=79, Dumper=MyDumper)
    

    【讨论】:

    【解决方案3】:

    PyYAML 文档只简要讨论了dump() 参数,因为没有太多要说的。 PyYAML 不提供这种控件。

    为了允许在加载的 YAML 中保留这些空(和注释)行,我开始开发 ruamel.yaml 库,它是停滞的 PyYAML 的超集,具有 YAML 1.2 兼容性,添加了许多功能并修复了错误。使用ruamel.yaml,您可以:

    import sys
    import ruamel.yaml
    
    yaml_str = """\
    - key_1: value_a
      key_2: value_b
    
    - key_1: value_c
      key_2: value_d
    
    - key_1: value_x  # a few before this were ellipsed
      key_2: value_y
    """
    
    yaml = ruamel.yaml.YAML()
    data = yaml.load(yaml_str)
    yaml.dump(data, sys.stdout)
    

    并得到与输入字符串完全相同的输出(包括注释)。

    您还可以从头开始构建您想要的输出:

    import sys
    import ruamel.yaml
    
    yaml = ruamel.yaml.YAML()
    list_of_dicts = yaml.seq([ { 'key_1': 'value_a', 'key_2': 'value_b'},
                               { 'key_1': 'value_c', 'key_2': 'value_d'},
                               { 'key_1': 'value_x', 'key_2': 'value_y'}  ])
    
    for idx in range(1, len(list_of_dicts)):
        list_of_dicts.yaml_set_comment_before_after_key(idx, before='\n')
    
    ruamel.yaml.comments.dump_comments(list_of_dicts)
    yaml.dump(list_of_dicts, sys.stdout)
    

    使用yaml.seq() 的转换对于创建一个允许通过特殊属性附加空行的对象是必要的。

    该库还允许在字符串、int 格式(十六进制、八进制、二进制)和浮点数上保存/轻松设置引号和文字样式。以及用于映射和序列的单独缩进规范(尽管不是针对单个映射或序列)。

    【讨论】:

    • 很好的答案,为你自己改进的 yaml 解析器分叉而欢呼,我现在就去尝试一下,因为我现在自己正在研究转储格式问题。不过我突然想到,pyyaml 自从这篇文章以来一直在更新。从那以后,他们在格式方面有所改进吗?或者您的任何贡献是否已合并到 pyyaml 中?
    • 两个问题都没有。我所知道的唯一真正的机会是,如果没有 100% 的输入控制,PyYAML 的 load() 将不再那么危险。它仍然是 YAML 1.1,多个长期存在的错误仍未修复。更改似乎主要是为了与较新版本的 Python 兼容。我认为我的(和所有其他 PR)随着从 bitbucket 到 github 的迁移而被丢弃(如果我在修正 PyYAML 恕我直言,这一努力会更好)。 ruamel.yaml 现在可以在项目指示符 (-) 之后控制额外的换行符:.compact_seq_seq resp。 .compact_seq_map
    猜你喜欢
    • 2014-01-15
    • 2014-12-31
    • 2021-05-20
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    • 2021-09-22
    • 1970-01-01
    相关资源
    最近更新 更多