2012-03-31 55 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