Python dataclass 如何让传入的 field 为 None 时自动转化为默认值?

发布时间 2023-09-25 11:18:29作者: BuckyI

一般 dataclass 的字段可以设置 default 或 default_factory 生成默认值,当传入参数时,默认值不会生效。但是,有些情况下受限于外部调用环境,某些参数缺失时,以 None 的形式存在,而非创建 dataclass 实例时不传入参数。这种情况下,可能会希望传入的 None 被识别到并转化为默认值。

from dataclasses import dataclass, field
@dataclass
class Specs:
    a: str
    b: str = field(default='Bravo')
    c: str = field(default='Charlie')

print(Specs(1)) # Specs(a=1, b='Bravo', c='Charlie')
print(Specs(1, None, None)) # Specs(a=1, b=None, c=None)

参考资料:

__post__init__

可以考虑在 __post__init__ 方法中对 None 进行处理,但是存在代码冗余的缺陷,即会重复默认值的生成代码(在属性声明时 field(default=xxx) 已经声明了默认值),如果只在 __post__init__ 中生成默认值,如下面代码所示,就会丢失掉默认值的类型提示。

@dataclass
class Specs2:
    a: str
    b: str
    c: str

    def __post_init__(self):
        if self.b is None:
            self.b = 'Bravo'
        if self.c is None:
            self.c = 'Charlie'

使用类名访问类属性(默认值)

下面的方法看起来简洁很多,但是适用条件也比较有限,即默认值需要是直接指定的,而非使用 field 指定。

@dataclass
class Specs1:
    a: str
    b: str = 'Bravo'
    c: str = 'Charlie'
a = 'Apple'
b = None
c = 'Potato'
specs = Specs1(a=a, b=b or Specs1.b, c=c or Specs1.c)
>>> specs
Specs1(a='Apple', b='Bravo', c='Potato')

我的方法

这是我最后选择的方法,给 dataclass 添加一个 from_dict 的方法,好处是,可以对于实例化时传入参数的合法性进行检验,调用端只需要提供字典类型的输入即可。


from dataclasses import dataclass, field

@dataclass
class Specs:
    a: str
    b: str = 'Bravo'
    c: str = 'Charlie'
    
    @staticmethod
    def from_dict(data: dict):
        data = {
            k: v for k, v in data.items()
            if k in ['a', 'b', 'c'] and v is not None
        }
        return Specs(**data)

# 通过某种方式获得以下参数
a = 'Apple'
b = None
c = 'Potato'
Specs.from_dict({'a': a, 'b': b, 'c': c})