【问题标题】:Python dataclass: can you set a default default for fields?Python数据类:您可以为字段设置默认默认值吗?
【发布时间】:2020-02-21 13:04:37
【问题描述】:

我想创建一个数据类基类,其中子类中的所有字段都是自动可选的,默认为无(如果没有提供默认值)。

以下代码...似乎几乎可以满足我的要求,但并不完全。它的错误方式与我从未编写过 __init_subclass__ 的方式相同(即抱怨未填充的参数)......也许是因为我的代码在数据类魔法发生之后运行

@dataclass(order=True, frozen=True)
class BaseDictKey:
    def __init_subclass__(cls, *args, **kwargs):
        super().__init_subclass__(*args, **kwargs)
        # noinspection PyUnresolvedReferences
        for field in cls.__dataclass_fields__.values():
            field.default = None if field.default is None else field.default
            field.type = typing.Union[field.type, NoneType]

@dataclass(order=True, frozen=True)
class ScoreDictKey(BaseDictKey):
    catalog: str  # magically becomes catalog: Optional[str] = None
    dataset: str = 'foo'  # magically becomes dataset: Optional[str] = 'foo'

(如果你想知道我为什么想要这个,我有另一个基类,它使用这些 BaseDictKeys 期望子类中的任何和所有字段都是可选的。我想我可以引发异常如果我检测到某些东西不是可选的,但这看起来更丑。)

这在 Python 3.7+ 中可行吗?

【问题讨论】:

  • 请描述“不完全”的方式,这样我们就不必猜测了。
  • 编辑了原文。这是我的更改:它的错误方式与我从未编写过 __init_subclass__ 的方式相同(即抱怨未填充的参数)......也许是因为我的代码在数据类魔法发生后运行?
  • 这条线应该做什么? field.default = None if field.default is None else field.default
  • 如果你看一下 ScoreDictKey.dataset,它应该更有意义,但基本上:如果提供了默认值,不要用 None 覆盖它,但如果没有提供任何内容,请将默认设置为没有。我没有这样做 None if not field.default else field.default 因为这不适用于 falsy-non-None 默认值(0、''、[] 等)

标签: python python-3.x python-dataclasses


【解决方案1】:

我找到了一种修改类__annotations__字段以使字段可选并直接在类上设置属性以提供默认值None的方法:

from dataclasses import dataclass

import typing


@dataclass(order=True, frozen=True)
class BaseDictKey:
    def __init_subclass__(cls, *args, **kwargs):
        for field, value in cls.__annotations__.items():
            cls.__annotations__[field] = typing.Union[value, None]
            if not hasattr(cls, field):
                setattr(cls, field, None)
        super().__init_subclass__(*args, **kwargs)


@dataclass(order=True, frozen=True)
class ScoreDictKey(BaseDictKey):
    catalog: str  # magically becomes catalog: Optional[str] = None
    dataset: str = 'foo'  # magically becomes dataset: Optional[str] = 'foo'

    def __init__(self, *args, **kwargs):  # to get rid of PyCharm warning
        pass


c = ScoreDictKey()
print(c.catalog, c.dataset)  # None foo

【讨论】:

  • 谢谢!这是那里的90%!在运行时工作顺利。然而...... PyCharm 不够聪明,无法意识到已经做了什么......它错误地突出显示了空括号并说 Parameter 'catalog_str' unfilled. 我唯一能想到的就是手动生成 init 函数,听起来很痛苦。
  • @user3534080 摆脱 PyCharm 警告我发现当前解决方案只有一种方法:将空 def __init__(self, *args, **kwargs): pass 添加到子类定义中。
猜你喜欢
  • 2011-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-08
  • 2022-01-02
  • 2018-11-17
  • 1970-01-01
相关资源
最近更新 更多