2012-03-31 116 views
1

所以我有一個yaml文件,有很多瑣事問題和答案列表。然而,每當我嘗試加載這個文件並用pyyaml轉儲python中的內容時,它會將它們倒轉回去。我不確定是否是我的yaml文件,或者我是否在圖書館做錯了什麼。PyYaml傾倒東西

比方說,我的問題/答案對一個看起來像這樣在YAML文件 -

{"question": "What is the name of this sequence of numbers: 1, 1, 2, 3, 5, 8, 13, ...", 
"answer": ["The Fibonacci Sequence", "The Padovan Sequence", "The Morris Sequence"]} 

當我使用對Python字典yaml.dump(),它甩掉這個 -

answer: [fibonacci, padovan, morris]\nquestion: 'what sequence is this: 1, 1, 2, 3, 5, 8, 13, ...'\n" 

我期待這一點 -

- question: "What is the name of this sequence of numbers: 1, 1, 2, 3, 5, 8, 13, ..." 
    answer: ["The Fibonacci Sequence", "The Padovan Sequence", "The Morris Sequence"] 

難道我做錯了什麼嗎?

回答

2

YAML關聯數組(和python字典)不保留其元素的順序。

但是,如果順序進口則YAML定義ordered map !!omap其PyYAML由默認解析成元組的列表,如:

>>> yaml.load('''!!omap 
... - a: foo 
... - b: bar''') 
[('a','foo'),('b','bar')] 

This answer提供了有關如何將!!omap放進去加載到Python OrderedDict一些細節。

+0

謝謝,所以我做錯了什麼,我只是不知道它是什麼。 – 2012-03-31 00:42:54

1

如果它將它們加載爲字典,它們的順序是任意的。字典不是有序的容器。

+0

我知道,重要的是怎麼把它們扔掉。我看到的這兩個字符串都沒有表示爲字符串或格式正確的答案(與第一個字符相同) – 2012-03-31 00:21:17

+0

@Matt,PyYAML基本上是YAML加載器/自卸器的參考實現,所以(特別是在這種情況下是一種常見的操作),其輸出將按照標準進行。 – huon 2012-03-31 00:34:11

6

我在這裏有一個有點不同的答案。如果由於可讀性以外的原因,元素的順序對您很重要,dbaupp的答案是正確的。如果您希望問題在回答之前出現的唯一原因是爲了使文件更具人類可讀性,那麼您不需要使用!! omap,而是可以使用自定義表示符來獲取所需的訂單。

首先,你的問題與自卸車傾銷沒有 - 在前面是因爲你只傾銷一個單一的映射,而不是他們的列表。把你的字典放在一個列表中,這將被修復。所以我們開始:

d = [{"question": "What is the name of this sequence of numbers: 1, 1, 2, 3, 5, 8, 13, ...", 
"answer": ["The Fibonacci Sequence", "The Padovan Sequence", "The Morris Sequence"]}] 

現在,我們有我們所要的輸出是一個特定的順序,所以我們指定,並轉換爲OrderedDict與秩序:

from collections import OrderedDict 
order = ['question', 'answer'] 
do = [ OrderedDict(sorted(z.items(), key=lambda x: order.index(x[0]))) for z in d ] 

接下來,我們需要使PyYAML知道如何處理OrderedDict。在這種情況下,我們不希望它成爲!! omap,我們只想要一個具有特定順序的映射。對於我不清楚的一些動機,如果您給dumper.represent_mapping一個字典或任何帶有items屬性的東西,它會在轉儲之前對這些項目進行排序,但是如果您給它的項目()的輸出(例如(鍵列表,值)元組),它不會。因此,我們可以使用

def order_rep(dumper, data): 
    return dumper.represent_mapping(u'tag:yaml.org,2002:map', data.items(), flow_style=False) 
yaml.add_representer(OrderedDict, order_rep) 

然後,我們從print yaml.dump(do)輸出端起來就是:

- question: 'What is the name of this sequence of numbers: 1, 1, 2, 3, 5, 8, 13, ...' 
    answer: [The Fibonacci Sequence, The Padovan Sequence, The Morris Sequence] 

有許多不同的方式可以這樣做。使用OrderedDict實際上並不是必要的,你只需要問題/答案對可以是一些你可以寫代表的類。

再次,要意識到這只是爲了人類的可讀性和審美目的。這裏的順序不會有任何YAML的意義,因爲如果你使用!! omap的話。看起來這對你來說可讀性最重要。

4

如果如果願意在轉儲,下面的代碼的順序可以用來

import yaml 

class MyDict(dict): 
    def to_omap(self): 
     return [('question', self['question']), ('answer', self['answer'])] 

def represent_omap(dumper, data): 
    return dumper.represent_mapping(u'tag:yaml.org,2002:map', data.to_omap()) 

yaml.add_representer(MyDict, represent_omap) 

questions = [ 
    MyDict({'answer': 'My name is Bob.', 'question': 'What is your name?'}), 
    MyDict({'question': 'How are you?', 'answer': 'I am fine.'}), 
] 
print yaml.dump(questions, default_flow_style=False) 

輸出是:

- question: What is your name? 
    answer: My name is Bob. 
- question: How are you? 
    answer: I am fine. 
+0

+1這是整潔,運作良好。我喜歡在'MyDict'上將'representsenter'作爲'@ staticmethod'來保持一致。所以你可以改爲'yaml.add_representer(MyDict,MyDict.representer)'。 – Day 2013-08-06 16:37:12

+0

但是,當用'yaml.safe_dump'轉儲時,這不起作用。任何想法如何我可以使用'safe_dump' **和**一個自定義代表如上?我收到一個異常:'yaml.representer.RepresenterError:不能代表一個對象:{'answer':'我的名字是鮑勃','問題':'你叫什麼名字?'} – Day 2013-08-06 16:39:46

+0

回答我自己以前的評論:使用'yaml.SafeDumper.add_representer(...)'而不是'yaml.add_representer(...)' – Day 2013-08-06 16:47:25