请参阅下面显示的这个简单的代码块。我的目标是使用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'})})
答案 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)