如何使用ruamel.yaml将字典与基于defaultdict的字典打印为yaml文件?

时间:2018-06-01 11:53:24

标签: ruamel.yaml

请参阅下面显示的这个简单的代码块。我的目标是使用defaultdict提出一个相对简单的字典,并进一步将结果打印为yaml文件。

当我手动定义字典时,它似乎工作正常,YAML完全按照我想要的方式显示,但当我使用defaultdict来提出字典时,我收到一条错误消息,不幸的是,我无法解读。

当我将字典打印为JSON时,它会输出完全相同的输出。我缺少什么?

import sys,ruamel.yaml
import json
from collections import defaultdict

def dict_maker():
    return defaultdict(dict_maker)

S = ruamel.yaml.scalarstring.DoubleQuotedScalarString
app = "someapp"

d = {'beats':{'name':S(app), 'udp_address':S('239.1.1.1:10101')}}

foo = dict_maker()
foo["beats"]["name"] = S(app)
foo["beats"]["udp_address"] = S("239.1.1.1:10101")

print "Regular dictionary"
print json.dumps(d, indent=4)

print "defaultdict dictionary"
print json.dumps(foo, indent=4)

print "dictionary as a yaml\n"
ruamel.yaml.dump(d, sys.stdout, Dumper=ruamel.yaml.RoundTripDumper)

print "defaultdict dictionary as a yaml\n"
ruamel.yaml.dump(foo, sys.stdout, Dumper=ruamel.yaml.RoundTripDumper)

错误消息

raise RepresenterError("cannot represent an object: %s" % data)
ruamel.yaml.representer.RepresenterError: cannot represent an object: defaultdict(<function dict_maker at 0x7f1253725a28>, {'beats': defaultdict(<function dict_maker at 0x7f1253725a28>, {'name': u'someapp', 'udp_address': u'239.1.1.1:10101'})})

1 个答案:

答案 0 :(得分:0)

在引用Python dict时,您似乎使用了“词典”这个词。然而,没有“基于默认词典的词典”,这意味着foo之后

foo = dict_maker()

将是dict,当然不是:foo是基于defaultdict的{​​{1}}(也就是说你的写法完全相反) )。

JSON转储它并不奇怪,因为它不能愚蠢地转储键值对,就好像它是dict一样。但是当你尝试加载那个JSON时,你会发现它是多么无用,你无法继续使用它(至少不是按照预期的方式):

dict

以上投掷:import sys import json from collections import defaultdict import io def dict_maker(): return defaultdict(dict_maker) app = "someapp" foo = dict_maker() foo["beats"]["name"] = app foo["beats"]["udp_address"] = "239.1.1.1:10101" io = io.StringIO() json.dump(foo, io, indent=4) io.seek(0) bar = json.load(io) bar['otherapp']['name'] = 'some_alt_app' print(bar['beats']['udp_address']) 。这是因为JSON没有保留所需的所有信息。

但是,如果你使用不安全的YAML转储器,那么KeyError: 'otherapp'可以转储并加载这个罚款:

ruamel.yaml

这不会引发错误,因为import sys from ruamel.yaml import YAML from collections import defaultdict import io def dict_maker(): return defaultdict(dict_maker) app = "someapp" yaml = YAML(typ='unsafe') foo = dict_maker() foo["beats"]["name"] = app foo["beats"]["udp_address"] = "239.1.1.1:10101" io = io.StringIO() yaml.dump(foo, io) io.seek(0) print(io.getvalue()) bar = yaml.load(io) bar['otherapp']['name'] = 'some_alt_app' print(bar['beats']['udp_address']) 再次是bar defaultdict作为默认的函数。以上打印

dict_maker

正如您所料。

239.1.1.1:10101 不支持这种开箱即用,因为它基于RoundTripDumper/Loader,它不能转储/加载像SafeDumper/Loader这样的任意Python实例及其defaultdict函数引用。启用它会使加载不安全。

因此,如果您需要使用dict_maker,您应该为RoundTripDumper或其子类添加一个表示符(也可以为defaultdict添加一个表示符)。为了能够加载它,您还需要构造函数。如何做到这一点在文档(Dumping Python classes

中有所描述