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

PyYaml加载含嵌套类的字符串失败,如何修复类路径问题?

解决PyYAML序列化/反序列化嵌套类的问题

你遇到的问题确实很典型:PyYAML默认处理嵌套类时,只会用类的短名称(__name__)生成YAML标签,而不是包含外层类的完整限定名(__qualname__),导致反序列化时找不到嵌套类的定义。

核心原因

PyYAML序列化对象时,默认生成的标签是!!python/object:{module}.{class.__name__},但嵌套类的__name__只有NestedChild,而它的实际完整路径应该是NotWorkingParent.NestedChild,对应标签应该是!!python/object:__main__.NotWorkingParent.NestedChild。反序列化时,PyYAML在__main__模块下找NestedChild,自然找不到,因为它是NotWorkingParent的内部类。

通用解决方案:自定义代表器和构造器

下面是一个通用的解决方法,通过自定义PyYAML的代表器(负责序列化)和构造器(负责反序列化),让它正确处理嵌套类的完整路径:

import yaml
from yaml.representer import Representer
from yaml.constructor import Constructor

class NotWorkingParent:
    class NestedChild:
        def __init__(self):
            self.nc1 = 11
            self.nc2 = 22
    def __init__(self):
        self.p1 = 11
        self.p2 = 22
        self.nc = NotWorkingParent.NestedChild()

# 自定义代表器:用__qualname__生成完整类路径的标签
def nested_class_representer(dumper, obj):
    # 获取类的完整限定名(包含外层类)
    full_class_path = f"{obj.__class__.__module__}.{obj.__class__.__qualname__}"
    return dumper.represent_mapping(f"!!python/object:{full_class_path}", obj.__dict__)

# 自定义构造器:根据完整类路径找到嵌套类并实例化
def nested_class_constructor(loader, node):
    # 从标签中提取完整类路径
    full_class_path = node.tag.split(':')[-1]
    # 拆分模块名和类的限定名
    module_name, class_qualname = full_class_path.rsplit('.', 1)
    # 导入模块
    module = __import__(module_name, fromlist=[''])
    # 逐层获取嵌套类
    target_class = module
    for part in class_qualname.split('.'):
        target_class = getattr(target_class, part)
    # 创建实例并加载属性
    instance = target_class.__new__(target_class)
    loader.construct_mapping(node, deep=True)
    instance.__dict__.update(node.value)
    return instance

# 注册自定义逻辑到PyYAML
yaml.add_representer(object, nested_class_representer)
yaml.add_constructor('!!python/object:', nested_class_constructor)

# 测试序列化与反序列化
parent = NotWorkingParent()
yaml_parent_str = yaml.dump(parent)
print("**** yaml_parent_str ***")
print(yaml_parent_str)
print("**** parent_from_yaml_str ***")
# 注意:使用FullLoader避免安全警告
parent_from_yaml_str = yaml.load(yaml_parent_str, Loader=yaml.FullLoader)
print(parent_from_yaml_str)
print(f"嵌套类实例类型:{type(parent_from_yaml_str.nc)}")
print(f"嵌套类属性nc1的值:{parent_from_yaml_str.nc.nc1}")

代码解释

  1. 代表器逻辑:利用类的__qualname__属性(Python 3.3+支持)获取嵌套类的完整路径,生成正确的YAML标签,确保序列化后的内容包含NotWorkingParent.NestedChild的完整路径。
  2. 构造器逻辑:解析标签中的完整类路径,逐层遍历模块和外层类,最终找到嵌套类的定义,然后创建实例并加载属性。
  3. 注册逻辑:将自定义的代表器和构造器绑定到PyYAML,让所有对象的序列化/反序列化都使用这套逻辑,适合处理任意嵌套类。

运行结果

执行上述代码后,你会看到序列化后的YAML标签已经是完整路径:

**** yaml_parent_str ***
!!python/object:__main__.NotWorkingParent
nc: !!python/object:__main__.NotWorkingParent.NestedChild {nc1: 11, nc2: 22}
p1: 11
p2: 22
**** parent_from_yaml_str ***
<__main__.NotWorkingParent object at 0x000001F8D7A205E0>
嵌套类实例类型:<class '__main__.NotWorkingParent.NestedChild'>
嵌套类属性nc1的值:11

针对特定嵌套类的简化方案

如果你只需要处理某个特定的嵌套类,也可以手动注册该类的序列化/反序列化逻辑,代码更简洁:

import yaml

class NotWorkingParent:
    class NestedChild:
        def __init__(self):
            self.nc1 = 11
            self.nc2 = 22
    def __init__(self):
        self.p1 = 11
        self.p2 = 22
        self.nc = NotWorkingParent.NestedChild()

# 手动注册NestedChild的代表器
yaml.add_representer(NotWorkingParent.NestedChild, 
    lambda dumper, obj: dumper.represent_mapping(
        f"!!python/object:{__name__}.NotWorkingParent.NestedChild", 
        obj.__dict__
    )
)
# 手动注册NestedChild的构造器
yaml.add_constructor(
    f"!!python/object:{__name__}.NotWorkingParent.NestedChild",
    lambda loader, node: loader.construct_yaml_object(node, NotWorkingParent.NestedChild)
)

# 测试
parent = NotWorkingParent()
yaml_parent_str = yaml.dump(parent)
parent_from_yaml_str = yaml.load(yaml_parent_str, Loader=yaml.FullLoader)
print(parent_from_yaml_str)
print(type(parent_from_yaml_str.nc))

这种方法不需要通用逻辑,适合只处理少量特定嵌套类的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:58:56