ruamel.yaml - 如何使用 ruamel.yaml 将字典与基于 defaultdict 的字典打印为 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'})})
解决方案
在引用 Python 时,您似乎使用了“字典”一词dict
。然而,没有“基于默认字典的字典”之类的东西,这意味着foo
之后
foo = dict_maker()
将是 a dict
,当然不是:foo
is a defaultdict
which is dict
based(即与您所写的完全相反)。
JSON 转储这个并不奇怪,因为它只能愚蠢地转储键值对,就好像它是一个dict
. 但是当您尝试重新加载该 JSON 时,您会发现这是多么无用,您无法继续使用它(至少不是以预期的方式):
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'])
以上抛出:KeyError: 'otherapp'
. 那是因为 JSON 没有保留所有需要的信息。
但是,如果您使用不安全的 YAML 转储程序,则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
或其子类(也可能添加一个代表dict_maker
)。为了能够加载它,您还需要构造函数。文档中描述了如何做到这一点(转储 Python 类)
推荐阅读
- bash - 对于具有数十万块数据的文件,如何扫描每个块以检查它是否包含 X 行?
- c# - RequestDelegate 是一个端点吗?
- android - macOS Catalina 上的 AOSP 存储库同步失败
- php - php静态函数类保存以前的函数调用数据
- java - 无法使用 XPath 定位嵌套文本
- c++ - 当 Table1.COL = Table2.Col 时从 Table1 中删除行
- visual-studio-2019 - 当 Configuration 设置为 RELEASE 时,为什么 Visual Studio 2019 在构建期间会在 DEBUG 文件夹中查找文件?
- firebase - Firebase 规则限制写入不起作用
- css - 如何使 Ant Design 表响应式
- swift - 在 Combine 中按顺序链式网络调用