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

如何让ruamel.yaml识别NamedTuple为tuple以避免序列化报错?

问题:ruamel.yaml无法正确序列化NamedTuple子类Loc

以下是测试代码:

import sys
from typing import NamedTuple

import ruamel.yaml as ryaml


class Loc(NamedTuple):
    lat: float
    long: float


data = {
    "APAC": {
        "rating": 5,
        "leads": ["Jane", "John"],
        "locs": [Loc(1.0, 1.0), Loc(2.0, 2.0)],
    },
    "EMEA": {
        "rating": 5,
        "leads": ["Jane", "Jack"],
        "locs": [Loc(3.0, 3.0), Loc(4.0, 4.0)],
    }
}


def main():
    # 验证Loc实例确实是tuple子类
    assert all(map(lambda o: isinstance(o, tuple), data["APAC"]["locs"]))
    assert all(map(lambda o: isinstance(o, tuple), data["EMEA"]["locs"]))

    yml = ryaml.YAML()
    yml.register_class(Loc)
    yml.dump(data, sys.stdout)


if __name__ == '__main__':
    main()

执行后抛出如下错误:

File "C:\Repos\@Venv\myproj-cpy3.11-1\Lib\site-packages\ruamel\yaml\representer.py", line 1090, in represent_yaml_object
    anchor = state.pop(Anchor.attrib, None)
             ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'pop'

直接使用原生tuple替代Loc()则无错误,环境为CPython 3.11 + ruamel.yaml==0.17.21。


解决方案

方法一:自定义Representer,将Loc实例转为tuple序列化

不需要调用yml.register_class(Loc),而是给Loc类添加自定义的序列化逻辑,让ruamel.yaml按tuple规则处理:

修改后的main函数:

def main():
    assert all(map(lambda o: isinstance(o, tuple), data["APAC"]["locs"]))
    assert all(map(lambda o: isinstance(o, tuple), data["EMEA"]["locs"]))

    yml = ryaml.YAML()
    
    # 自定义Loc的序列化逻辑,转为tuple处理
    def represent_loc(dumper, loc_instance):
        return dumper.represent_tuple(tuple(loc_instance))
    
    yml.representer.add_representer(Loc, represent_loc)
    yml.dump(data, sys.stdout)

方法二:给Loc类指定yaml_tag为tuple类型

直接在Loc类中添加yaml_tag属性,指定为!!python/tuple,ruamel.yaml会自动用tuple的序列化规则处理:

修改后的Loc类:

class Loc(NamedTuple):
    lat: float
    long: float
    yaml_tag = '!!python/tuple'

然后main函数中去掉yml.register_class(Loc)即可:

def main():
    assert all(map(lambda o: isinstance(o, tuple), data["APAC"]["locs"]))
    assert all(map(lambda o: isinstance(o, tuple), data["EMEA"]["locs"]))

    yml = ryaml.YAML()
    yml.dump(data, sys.stdout)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 12:55:17