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

Python dataclass继承层级添加带默认值属性的问题及解决方案

给定如下Python dataclass继承层级:

@dataclass
class A:
    a: str
    aa: str


@dataclass
class B(A):
    b: str


@dataclass
class C(A):
    c: str


@dataclass
class D(B, C):
    d: str

是否存在通用方法,可以在该继承层级中插入带默认值的属性?例如如下写法会为类D触发Non-default argument(s) follows default argument(s) defined in 'B'错误:

@dataclass
class A:
    a: str
    aa: str


@dataclass
class B(A):
    b: str = 'b'  # <-- 新增默认值


@dataclass
class C(A):
    c: str


@dataclass
class D(B, C):
    d: str

现有两种常见解决方案均存在明显缺陷:

  • 为层级中所有属性设置None默认值,再添加__post_init__()校验逻辑,若属性为None则抛出异常。该方案不够优雅,且需要在层级的每个dataclass中重复实现校验逻辑。
  • 拆分继承分支,分别创建带默认值和不带默认值的两个独立继承分支。该方案的缺点是会快速生成大量类,导致继承层级臃肿。
补充更新

笔者自行实现了一个meta_dataclass装饰器,通过封装处理解决了上述问题,虽然可能存在部分规范冲突,但欢迎大家优化改进。
该装饰器允许在meta_dataclass继承层级的任意位置添加带默认值的属性,设计思路参考上述双分支继承方案,区别是将额外类的创建逻辑封装在meta_dataclass装饰器内部,无需开发者手动维护双分支。当前缺点是meta_dataclass只能继承自其他meta_dataclass。

完整实现代码:

from dataclasses import make_dataclass, MISSING, field


class Dataclass:
    pass


class Declarations:
    pass


class Definitions:
    pass


def meta_dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False):
    def wrap(cls):
        declaration_bases = []
        definition_bases = []

        for base in cls.__bases__:
            if issubclass(base, Dataclass):
                declaration_bases += [c for c in base.__bases__ if issubclass(c, Declarations)]
                definition_bases += [c for c in base.__bases__ if issubclass(c, Definitions)]
            elif len(cls.__bases__) == 1 and base != object:
                raise ValueError(f'meta_dataclasses can only inherit from other meta_dataclasses. 'f'{cls} inherits from {base}')

        declaration_bases.append(Declarations)
        definition_bases.append(Definitions)

        fields = []
        if hasattr(cls, '__annotations__'):
            for field_name, field_type in cls.__annotations__.items():
                f = field(default=cls.__dict__[field_name]) if field_name in cls.__dict__ else field()
                fields.append((field_name, field_type, f))

        declarations = make_dataclass(cls_name=f'{cls.__name__}_Declarations',
                                      bases=tuple(declaration_bases),
                                      fields=[f for f in fields if isinstance(f[2].default, type(MISSING))],
                                      init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen)

        definitions = make_dataclass(cls_name=f'{cls.__name__}_Definitions',
                                     bases=tuple(definition_bases),
                                     fields=[f for f in fields if not isinstance(f[2].default, type(MISSING))],
                                     init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen)

        cls_wrapper = make_dataclass(cls_name=cls.__name__, fields=[], bases=(Dataclass, definitions, declarations),
                                     namespace=cls.__dict__, init=init, repr=repr, eq=eq, order=order,
                                     unsafe_hash=unsafe_hash, frozen=frozen)

        return cls_wrapper

    if cls is None:
        return wrap
    else:
        return wrap(cls)

使用示例:

@meta_dataclass
class A:
    a: str
    aa: str

@meta_dataclass
class B(A):
    b: str = 'b'

@meta_dataclass
class C(A):
    c: str

@meta_dataclass
class D(B, C):
    d: str


>>> help(D)
Help on class D in module types:

class D(__main__.Dataclass, D_Definitions, D_Declarations)
 |  D(a: str, aa: str, c: str, d: str, b: str = 'b') -> None
 |  
 |  D(a: str, aa: str, c: str, d: str, b: str = 'b')
 |  
 |  Method resolution order:
 |      D
 |      __main__.Dataclass
 |      D_Definitions
 |      B_Definitions
 |      C_Definitions
 |      A_Definitions
 |      __main__.Definitions
 |      D_Declarations
 |      B_Declarations
 |      C_Declarations
 |      A_Declarations
 |      __main__.Declarations
 |      builtins.object

实现逻辑示意图:
下图展示了@meta_dataclass装饰器对单个类的处理逻辑:
@meta_dataclass处理逻辑示意图


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 15:15:06