Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Python中向YAML文件添加注释_Python_Yaml_Ruamel.yaml - Fatal编程技术网

如何在Python中向YAML文件添加注释

如何在Python中向YAML文件添加注释,python,yaml,ruamel.yaml,Python,Yaml,Ruamel.yaml,我正在使用 代码如下所示: import ruamel.yaml from ruamel.yaml.comments import CommentedSeq d = {} for m in ['B1', 'B2', 'B3']: d2 = {} for f in ['A1', 'A2', 'A3']: d2[f] = CommentedSeq(['test', 'test2']) if f != 'A2': d2[f].f

我正在使用

代码如下所示:

import ruamel.yaml
from ruamel.yaml.comments import CommentedSeq

d = {}
for m in ['B1', 'B2', 'B3']:
    d2 = {}
    for f in ['A1', 'A2', 'A3']:
        d2[f] = CommentedSeq(['test', 'test2'])
        if f != 'A2':
            d2[f].fa.set_flow_style()
    d[m] = d2

    with open('test.yml', "w") as f:
        ruamel.yaml.dump(
            d, f, Dumper=ruamel.yaml.RoundTripDumper,
            default_flow_style=False, width=50, indent=8)
我只想在顶部添加评论,如:

# Data for Class A

在YAML数据之前。

在带有块的
中,您可以向文件写入任何内容。由于您只需要在顶部添加注释,因此在调用ruamel之前,请添加对
f.write()
的调用:

with open('test.yml', "w") as f:
    f.write('# Data for Class A\n')
    ruamel.yaml.dump(
        d, f, Dumper=ruamel.yaml.RoundTripDumper,
        default_flow_style=False, width=50, indent=8)

这在原则上是可能的,因为您可以往返于这样的“文件开始”注释,但在当前的ruamel.yaml 0.10中,它没有得到很好的支持,而且在“从头开始”时(即不更改现有文件)肯定不受支持。在底部是一个简单的相对不错的解决方案,但我想首先介绍一个丑陋的解决方法和一个逐步实现的方法

丑陋的:
最糟糕的方法是在将YAML数据写入文件之前,将注释添加到文件中。即插入:

f.write('# Data for Class A\n')
就在
ruamel.yaml.dump(…)

一步一步地
要在数据结构上插入注释,因此无需进行上述攻击,请首先 需要确保您的
d
数据是
CommentedMap
类型。如果 通过将注释过的YAML加载回
c

import ruamel.yaml
from ruamel.yaml.comments import Comment, CommentedSeq, CommentedMap

d = CommentedMap()             # <<<<< most important
for m in ['B1', 'B2', 'B3']:
    d2 = {}
    for f in ['A1', 'A2', 'A3']:
        d2[f] = CommentedSeq(['test', 'test2'])
        if f != 'A2':
            d2[f].fa.set_flow_style()
    d[m] = d2

yaml_str = ruamel.yaml.dump(d, Dumper=ruamel.yaml.RoundTripDumper,
                            default_flow_style=False, width=50, indent=8)

assert not hasattr(d, Comment.attrib)  # no attribute on the CommentedMap

comment = 'Data for Class A'
commented_yaml_str = '# ' + comment + '\n' + yaml_str
c = ruamel.yaml.load(commented_yaml_str, Loader=ruamel.yaml.RoundTripLoader)
assert hasattr(c, Comment.attrib)  # c has the attribute
print c.ca                         # and this is what it looks like
print d.ca                         # accessing comment attribute creates it empty
assert hasattr(d, Comment.attrib)  # now the CommentedMap has the attribute
Comment
具有一个属性
Comment
,需要将该属性设置为2元素列表,该列表由下线注释(始终只有一个)和前一行注释列表(以
注释标记的形式)组成

要创建CommentToken,您需要一个(假)StartMark,告诉它从哪个列开始:

from ruamel.yaml.error import StreamMark
start_mark = StreamMark(None, None, None, 0, None, None)  # column 0
现在您可以创建令牌:

from ruamel.yaml.tokens import CommentToken

ct = CommentToken('# ' + comment + '\n', start_mark, None)
将令牌指定为CommentedMap上前面列表的第一个元素:

d.ca.comment = [None, [ct]]
print d.ca   # in case you want to check
给你:

Comment(comment=[None, [CommentToken(value='# Data for Class A\n')]],
  items={})
最后:

print ruamel.yaml.dump(d, Dumper=ruamel.yaml.RoundTripDumper)  
给出:

# Data for Class A
B1:
        A1: [test, test2]
        A3: [test, test2]
        A2:
        - test
        - test2
B2:
        A1: [test, test2]
        A3: [test, test2]
        A2:
        - test
        - test2
B3:
        A1: [test, test2]
        A3: [test, test2]
        A2:
        - test
        - test2
当然,您不需要创建
c
对象,这只是为了说明

您应该使用什么: 为了使整个练习变得更容易,您只需忘记细节,然后使用以下方法将其修补到
CommentedBase
一次:

from ruamel.yaml.comments import CommentedBase

def set_start_comment(self, comment, indent=0):
    """overwrites any preceding comment lines on an object
    expects comment to be without `#` and possible have mutlple lines
    """
    from ruamel.yaml.error import StreamMark
    from ruamel.yaml.tokens import CommentToken
    if self.ca.comment is None:
        pre_comments = []
        self.ca.comment = [None, pre_comments]
    else:
        pre_comments = self.ca.comments[1]
    if comment[-1] == '\n':
        comment = comment[:-1]  # strip final newline if there
    start_mark = StreamMark(None, None, None, indent, None, None)
    for com in comment.split('\n'):
        pre_comments.append(CommentToken('# ' + com + '\n', start_mark, None))

if not hasattr(CommentedBase, 'set_start_comment'): # in case it is there
    CommentedBase.set_start_comment = set_start_comment
然后做:

d.set_start_comment('Data for Class A')

这似乎需要做很多工作,只是为了解决调用
f.write()
的问题。有什么好处?这就是说,作为ruamel的内置部分,它似乎是有意义的,也许会向他们发送一个pull请求?@dimo414是的,如果它只是用于文件的顶部,那么它是开销。但如果你将它添加到数据中,然后使用列表中的数据并将其写出来,它就会起作用。我想以一种通用的方式将其包含在ruamel.yaml中,因为我是作者,所以不需要PR。因此,我将在下一次更新中上传到PyPI(10.1)。然后我可以把答案减少到最后一行;-)
d.set_start_comment('Data for Class A')