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

如何结合default_factory从YAML创建Python数据类?

解决dataclass结合pyyaml使用field(default_factory)时默认值不生效的问题

当使用Python dataclass继承yaml.YAMLObject加载YAML内容时,普通默认值(如age: int = 0)可正常生效,但用field(default_factory=...)定义的属性(比如动态生成的空列表),在YAML未提供该属性时会触发AttributeError,提示对象缺少对应属性。

示例报错代码:

from dataclasses import dataclass, field
import yaml

@dataclass
class Person2(yaml.YAMLObject):
    yaml_loader = yaml.SafeLoader
    yaml_tag = "!person2"

    name: str
    parents: list[str] = field(default_factory=list)
    age: int = 0

p2 = yaml.safe_load("""
!person2
name: Jacques Toto
age: 100
""")
print(p2)

报错信息:

AttributeError: 'Person2' object has no attribute 'parents'

原因分析

yaml.YAMLObject的默认实例构造逻辑是:先创建类的空实例(不调用__init__),再将YAML解析得到的键值对直接赋值给实例属性。而dataclass中field(default_factory=...)的默认值是在__init__方法中通过调用工厂函数生成的,YAMLObject的构造逻辑跳过了__init__,因此未在YAML中定义的属性不会被初始化,导致缺失。


解决方案

核心思路是让pyyaml在创建实例时调用dataclass的__init__方法,触发默认值的生成逻辑,以下是两种常用实现方式:

方法1:重写from_yaml类方法

在dataclass中重写from_yaml方法,手动解析YAML数据后调用类的构造函数:

from dataclasses import dataclass, field
import yaml

@dataclass
class Person2(yaml.YAMLObject):
    """A Person, to be loaded from YAML"""
    yaml_loader = yaml.SafeLoader
    yaml_tag = "!person2"

    name: str
    parents: list[str] = field(default_factory=list)
    age: int = 0

    @classmethod
    def from_yaml(cls, loader, node):
        # 解析YAML节点为字典
        data = loader.construct_mapping(node, deep=True)
        # 调用类的__init__,自动处理所有dataclass默认值
        return cls(**data)

测试验证:

p2 = yaml.safe_load("""
!person2
name: Jacques Toto
age: 100
""")
print(p2)
# 输出:Person2(name='Jacques Toto', parents=[], age=100)

方法2:手动注册YAML构造器

不使用yaml.YAMLObject继承,直接给pyyaml注册自定义构造器,控制实例创建过程:

from dataclasses import dataclass, field
import yaml

@dataclass
class Person2:
    """A Person, to be loaded from YAML"""
    name: str
    parents: list[str] = field(default_factory=list)
    age: int = 0

def construct_person2(loader, node):
    # 解析YAML数据为字典
    data = loader.construct_mapping(node, deep=True)
    # 调用dataclass构造函数生成实例
    return Person2(**data)

# 给SafeLoader注册构造器
yaml.SafeLoader.add_constructor("!person2", construct_person2)

测试验证:

p2 = yaml.safe_load("""
!person2
name: Jacques Toto
age: 100
""")
print(p2)
# 输出:Person2(name='Jacques Toto', parents=[], age=100)

总结

两种方法都通过调用dataclass的__init__方法创建实例,确保field(default_factory=...)定义的默认值被正确初始化,避免属性缺失的错误。

内容的提问来源于stack exchange,提问作者Jean-Francois T.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 16:05:55