【问题标题】:YAML output format of pythonpython的YAML输出格式
【发布时间】:2014-01-29 00:27:20
【问题描述】:

我使用PyYaml 输出YAML 文件。但它重新排序了我的物品。喜欢关注

>>> yaml.dump({'3':5, '1':3})
"{'1': 3, '3': 5}\n"

我想得到"{'3': 5, '1': 3}\n"。我可以这样做吗

附言。我试过collections.OrderedDict。它的输出并不好。喜欢关注

>>> a= collections.OrderedDict()
>>> a['3']=1
>>> a['1']=2
>>> a['5']=2
>>> yaml.dump(a)
"!!python/object/apply:collections.OrderedDict\n- - ['3', 1]\n  - ['1', 2]\n  - ['5', 2]\n"

【问题讨论】:

  • Python dicts 是无序的。
  • 这与 YAML 没有太大关系,但与 Python 有关:尝试在控制台输入 {'3':5, '1':3}。 Python 字典是无序的。因此,您的首要任务是决定如何更改数据结构以保持顺序(无论是元组列表,还是collections.OrderedDict,或其他。)
  • @DSM 谢谢。但它将 !!python/objectapply:collections.OrderedDict 添加到我的 YAML 文件中
  • 是否有任何具体原因可以期待该输出?
  • @Huy Phan 是的。我希望我的 YAML 文件以特定顺序输出项目

标签: python pyyaml


【解决方案1】:

YAML 映射是无序的,Python 字典也是如此。读取文件的官方方式 并保持顺序是使用!!omap,但那些会在PyYAML中转换为元组,并且不像dict/ordereddict/OrderedDict那样容易更新。

如果您已经有一个读取并更新的 yaml 文件,您可以使用我的 ruamel.yaml 库,该库在往返模式下作为ordereddict 使用时读取映射并将它们作为普通映射写出(它还保留了 cmets )。

example 的用法是作为另一个问题的答案。

【讨论】:

    【解决方案2】:

    TL;DR:解决方案是在两行注释“看这里!”如果您接受输出将是列表列表,则可以将 YAML 作为程序中的 dicts 处理并在存储的文件/文本中进行排序。

    如果您不介意 !!python/ordered_dict 或 !!omap 等丑陋的显式类型乱扔文件,那么您也可以走这条路。我的投票投给了!!omap,但我不确定有多少工具/库支持它(不过,我很确定支持 !!python/ordered_dict 的工具更少)。最终,您要处理两组独立的数据:dict 本身和定义键顺序的元数据。

    (在没有 !!python/ordered_dict 或 !!omap 乱码的情况下,有一些半神奇的方法可以在 YAML 中强制一个有序的字典,但是它们很脆弱,与字典的定义相矛盾,并且很可能会破坏为底层YAML 库不断发展。顺便说一下,这种情况对于 JSON 来说是相同的,因为 YAML 是 JSON 的超集,并且不保证密钥的顺序——这意味着当符合标准的工具/用户第一次弄乱文件时,变通方法会中断.)

    这篇文章的其余部分是示例/验证代码,并解释了为什么会这样。

    from __future__ import print_function
    import yaml
    
    # Setting up some example data
    d = {'name': 'A Project',
         'version': {'major': 1, 'minor': 4, 'patch': 2},
         'add-ons': ['foo', 'bar', 'baz']}
    
    # LOOK HERE!
    ordering = ['name', 'version', 'add-ons', 'papayas']
    ordered_set = [[x, d[x]] for x in ordering if x in d.keys()]
    # In the event you only care about a few keys,
    # you can tack the unspecified ones onto the end
    # Note that 'papayas' isn't a key. You can establish an ordering that
    # includes optional keys by using 'if' as a guard in the list comprehension.
    
    # Demonstration
    things = {'unordered.yaml': d, 'ordered.yaml': ordered_set}
    for k in things:
        f = open(k, 'w')
        f.write(yaml.dump(things[k], default_flow_style=False, allow_unicode=True))
        f.close()
    
    # Let's check the result
    output = []
    for k in things:
        f = open(k, 'r')
        output.append(dict(yaml.load(f.read())))
        f.close()
    
    # Should print 'OK'
    if output[0] == output[1]:
        print('OK')
    else:
        print('Something is wrong')
    

    创建的文件如下所示:

    ordered.yaml:

    - - name
      - A Project
    - - version
      - major: 1
        minor: 4
        patch: 2
    - - add-ons
      - - foo
        - bar
        - baz
    

    unordered.yaml:

    add-ons:
    - foo
    - bar
    - baz
    name: A Project
    version:
      major: 1
      minor: 4
      patch: 2
    

    这并没有像您希望的那样生成漂亮的 YAML 文档。也就是说,它可以将漂亮的 YAML 作为初始输入(耶!),并且编写从不漂亮的、有序的 YAML 到漂亮的、仍然有序的、dict 样式的 YAML 的脚本很简单(我把它留给你作为练习) .

    如果您有想要保留的键的顺序,请将其写入有序列表/元组。使用该列表生成列表的有序列表(但不是元组列表,因为您将在 YAML 中获得 !!python/tuple 类型,这很糟糕)。将其转储到 YAML。要像往常一样读回它,然后将该结构传递给 dict() ,然后您将返回到您开始使用的原始字典。如果您有一个需要保留其顺序的嵌套结构,您可能必须递归地降低该结构(这在代码中比在散文中解释更容易 - 这您可能已经知道了)。

    在此示例中,我希望文件中首先出现项目“名称”,然后是“版本”编号元素,然后是“附加组件”。通常,当您调用 dump() 时,PyYAML 按字母数字顺序对字典键进行排序,但这并不可靠,因为将来可能会发生变化,并且 YAML 标准中没有任何要求,所以我不能保证不同的 YAML 实用程序会以这种方式做事。 “附加组件”在“名称”之前,所以我有一个订购问题。所以我定义了我的顺序,然后打包一个有序列表,然后转储它。

    您要求对本质上无序的事物进行排序。字典是一个哈希表,内部排序专门用于搜索速度。这个顺序是你不应该弄乱的,因为如果明天发现了一种更快的实现字典的方法,运行时需要在不破坏每个人的代码的情况下实现它,而字典依赖于作为哈希表的有用抽象。

    同理,YAML 不是一种标记语言(毕竟它最初代表“Yaml Ain't a Markup Language”),它是一种数据格式。区别很重要。一些数据是有序的,比如元组和列表;有些不是,比如成袋的键值对(与哈希表略有不同,但在概念上相似)。

    我使用这种解决方案的递归版本来保证跨不同 YAML 实现的 YAML 输出,不是为了人类可读性,而是因为我在 YAML 中传递大量数据并且每个记录都必须使用密钥进行签名,并且每当使用字典/哈希时,不确定的顺序会阻止统一签名。

    【讨论】:

      猜你喜欢
      • 2012-08-28
      • 1970-01-01
      • 2019-06-29
      • 2021-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-06
      相关资源
      最近更新 更多