2013-05-28 91 views
18

我使用yaml.dump來輸出字典。它根據密鑰按字母順序打印每個項目。PyYAML dump可以以非字母順序排列項目嗎?

>>> d = {"z":0,"y":0,"x":0} 
>>> yaml.dump(d, default_flow_style=False) 
'x: 0\ny: 0\nz: 0\n' 

有沒有辦法控制鍵/值對的順序?

在我的特殊用例中,反向打印(恰巧)足夠好。儘管如此,我正在尋找一個答案,展示如何更精確地控制訂單。

我看過使用collections.OrderedDict,但PyYAML沒有(似乎)支持它。我也看過子類yaml.Dumper,但我一直無法弄清楚它是否有能力改變項目順序。

+1

看着源代碼,它看起來像它強制排序字典,所以你可能無法只調用'yaml.add_representer'。 – Blender

回答

32

可能有更好的解決方法,但在文檔或源代碼中找不到任何內容。


Python 2中(見註釋)

我子類OrderedDict,並使其返回不可排序項的列表:

from collections import OrderedDict 

class UnsortableList(list): 
    def sort(self, *args, **kwargs): 
     pass 

class UnsortableOrderedDict(OrderedDict): 
    def items(self, *args, **kwargs): 
     return UnsortableList(OrderedDict.items(self, *args, **kwargs)) 

yaml.add_representer(UnsortableOrderedDict, yaml.representer.SafeRepresenter.represent_dict) 

它似乎工作:

>>> d = UnsortableOrderedDict([ 
...  ('z', 0), 
...  ('y', 0), 
...  ('x', 0) 
... ]) 
>>> yaml.dump(d, default_flow_style=False) 
'z: 0\ny: 0\nx: 0\n' 

的Python 3或2(見註釋)

您也可以編寫自定義申述,但是,我不知道,如果你以後遇到的問題,因爲我從中剝離出來的一些風格檢查代碼:

import yaml 

from collections import OrderedDict 

def represent_ordereddict(dumper, data): 
    value = [] 

    for item_key, item_value in data.items(): 
     node_key = dumper.represent_data(item_key) 
     node_value = dumper.represent_data(item_value) 

     value.append((node_key, node_value)) 

    return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value) 

yaml.add_representer(OrderedDict, represent_ordereddict) 

但是,您可以使用原生OrderedDict類。

+0

非常好,我喜歡你的風格。我會用第一種解決方案,因爲我認爲它更清楚一點。我將不得不重新編譯字典,並且代理器中的'MappingNode'調用和奇怪的unicode字符串使它變得不透明(對我來說!)。謝謝! – mwcz

+0

@mwcz:第一個問題的唯一問題是子類OrderedDict',所以如果它工作,它的工作。 – Blender

+2

我不確定它是否是我的Python版本(3.4),但這不起作用。我在'yaml/representer.py:111'查看了源代碼,你可以看到'mapping = sorted(mapping)'。它使用'sorted'內建,而不是UnsortableList的'.sort()'方法。有任何想法嗎? –

2

我還在尋找一個問題的答案:「如何將轉儲映射與保存的順序?」我不能按照上面給出的解決方案,因爲我是pyyaml和python的新手。花了一些時間在pyyaml文檔和其他論壇上,我發現這一點。

您可以使用標籤

!OMAP

通過維護秩序轉儲映射。如果你想玩的順序,我認爲你必須去鍵:值

下面的鏈接可以幫助更好的理解。

https://bitbucket.org/xi/pyyaml/issue/13/loading-and-then-dumping-an-omap-is-broken

http://yaml.org/type/omap.html

+4

你可以添加幾行代碼作爲例子嗎?雖然這不僅僅是一個鏈接唯一的答案,但如果鏈接斷開,它不會留下太多的東西,並且示例代碼對於我們懶惰的人更方便。有關更多信息,請參閱有關StackOverflow樣式的此問題:http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers(此鏈接不太可能腐爛 ) –

1

有,只要你想,你需要做的就是這兩樣東西:

  • 你需要使用別的東西比dict,因爲它不保持訂購的物品
  • 您需要以適當的方式傾倒該替代物。¹

import sys 
import ruamel.yaml 
from ruamel.yaml.comments import CommentedMap 

d = CommentedMap() 
d['z'] = 0 
d['y'] = 0 
d['x'] = 0 

ruamel.yaml.round_trip_dump(d, sys.stdout) 

輸出:

z: 0 
y: 0 
x: 0 

¹這是使用ruamel.yaml一個YAML 1.2分析器,其中我的作者來完成。

0

這實在只是@ Blender答案的附錄。如果您在PyYAML來源看,在representer.py模塊,你會發現這個方法:

def represent_mapping(self, tag, mapping, flow_style=None): 
    value = [] 
    node = MappingNode(tag, value, flow_style=flow_style) 
    if self.alias_key is not None: 
     self.represented_objects[self.alias_key] = node 
    best_style = True 
    if hasattr(mapping, 'items'): 
     mapping = mapping.items() 
     mapping.sort() 
    for item_key, item_value in mapping: 
     node_key = self.represent_data(item_key) 
     node_value = self.represent_data(item_value) 
     if not (isinstance(node_key, ScalarNode) and not node_key.style): 
      best_style = False 
     if not (isinstance(node_value, ScalarNode) and not node_value.style): 
      best_style = False 
     value.append((node_key, node_value)) 
    if flow_style is None: 
     if self.default_flow_style is not None: 
      node.flow_style = self.default_flow_style 
     else: 
      node.flow_style = best_style 
    return node 

如果你只是刪除mapping.sort()線,然後將其保持在OrderedDict項目的順序。

更新:

我只注意到Fedora項目的EPEL回購有一個名爲python2-yamlordereddictloader包,並有一個爲Python 3爲好。該軟件包的上游項目可能是跨平臺的。

相關問題