You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用ruamel.yaml将普通字典与defaultdict字典输出为YAML文件?

问题原因与解决方案

你遇到的问题核心在于:ruamel.yaml的RoundTripDumper默认不识别collections.defaultdict这种自定义字典类型。虽然JSON序列化工具会自动把defaultdict当作普通dict处理,但ruamel.yaml的RoundTripDumper对类型的校验更严格——它需要明确知道如何序列化非原生的Python对象,遇到没有对应处理逻辑的类型时就会抛出RepresenterError。

为什么JSON可以正常输出?

JSON序列化时,只会遍历defaultdict的键值对,不会保留它的defaultdict类型信息,相当于自动把它降级成了普通字典,所以输出和原生dict完全一致。但ruamel.yaml的RoundTripDumper会检查对象的实际类型,发现是它没有预设处理规则的defaultdict,就会报错。

解决办法

这里有两种实用方案,你可以根据需求选择:

方案1:递归将嵌套defaultdict转为普通dict

因为你的defaultdict是多层嵌套结构(dict_maker生成的是递归的defaultdict),所以需要写一个递归函数,把所有层级的defaultdict都转换成原生dict:

def defaultdict_to_dict(d):
    if isinstance(d, defaultdict):
        # 递归处理每个值,把嵌套的defaultdict也转成dict
        d = {k: defaultdict_to_dict(v) for k, v in d.items()}
    return d

然后在dump前转换你的foo对象:

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

方案2:给ruamel.yaml注册defaultdict的表示器

如果你想继续使用defaultdict的便利性,不想每次手动转换,可以给RoundTripDumper注册一个自定义处理逻辑,让它知道如何序列化defaultdict:

from ruamel.yaml import representer

def represent_defaultdict(dumper, data):
    # 告诉dumper把defaultdict当作普通dict来序列化
    return dumper.represent_dict(data.items())

# 给RoundTripDumper注册这个处理逻辑
ruamel.yaml.RoundTripDumper.add_representer(defaultdict, represent_defaultdict)

注册完成后,你就可以直接dumpfoo了,不需要额外转换:

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

完整测试代码(以方案2为例)

把注册逻辑集成到你的代码里,就能正常输出YAML了:

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

def dict_maker():
    return defaultdict(dict_maker)

# 注册defaultdict的表示器
def represent_defaultdict(dumper, data):
    return dumper.represent_dict(data.items())

ruamel.yaml.RoundTripDumper.add_representer(defaultdict, represent_defaultdict)

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)

运行这段代码,就能得到和普通字典完全一致的YAML输出了。

内容的提问来源于stack exchange,提问作者MediocreTechie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:01:27