使用 dataclasses.MISSING 作为带有 Python 数据类的可选参数值?

Using dataclasses.MISSING as optional parameter value with a Python dataclass?

我想使 set 参数可选,但仍允许 None 为有效值。基于 the documentation,它建议 dataclasses.MISSING 可以使用默认值来帮助完成此操作。

As shown above, the MISSING value is a sentinel object used to detect if some parameters are provided by the user. This sentinel is used because None is a valid value for some parameters with a distinct meaning. No code should directly use the MISSING value.

但是通过如下使用:

import dataclasses
from dataclasses import dataclass, field

@dataclass
class Var:
    get: list
    set: list = dataclasses.MISSING

    def __post_init__(self):
        if self.set is dataclasses.MISSING:
            self.set = self.get

 print(Var(get=['Title']))  

我收到一个错误:

Traceback (most recent call last):
File "main.py", line 31, in <module>
print(Var(get=['Title']))
TypeError: __init__() missing 1 required positional argument: 'set'

不知道这样能不能用dataclasses.MISSING,所以干脆用专用的enum。因为它是一个enum,它保证只和它自己一样,所以它应该给你想要的东西:

from dataclasses import dataclass
from enum import Enum

_field_status = Enum("FieldStatus", "UNSET")

@dataclass
class Var:
    get: list
    set: list = _field_status.UNSET

    def __post_init__(self):
        if self.set is _field_status.UNSET:
            self.set = self.get

print(Var(get = [7]))

print(Var(get=[7], set=[8]))
print(Var(get=[7], set=None))

显然这会阻止用户将 set 设置为 _field_status.UNSET,但大概他们不需要这样做。

请注意,我有点困惑为什么 None 是暗示为 list 的某些内容的有效值,但原则是成立的。

No code should directly use the MISSING value.

上述部分在文档中注明是有原因的。因此,我们应该尽可能避免在我们的应用程序代码中使用 MISSING 用法(和导入)。在这种情况下,使用 MISSING 根本不适用于我们的用例。

假定的用法(通过避免直接使用 MISSING 标记值,而是使用 dataclasses.field(...):

from dataclasses import dataclass, field
from typing import Optional


@dataclass
class Var:
    get: list[str]
    set: Optional[list[str]] = field(default=None)


print(Var(get=['Title']))
# Var(get=['Title'], set=None)

但是MISSING实际用在哪里呢?

MISSINGdataclasses 模块在幕后使用的哨兵对象,因为它很神奇。

您可以检查 dataclasses.field 的源代码并在那里找到它的明确用法:

def field(*, default=MISSING, default_factory=MISSING, init=True, repr=True,
          hash=None, compare=True, metadata=None):

您会看到 default 等字段的声明默认值是 default=MISSING 而不是 default=None。这样做主要是为了识别用户是否实际将 defaultdefault_factory 的值传递给工厂函数 fields。例如,像我们在上面的例子中所做的那样传递 field(default=None) 是完全有效的;但是,由于默认值实际上是 MISSING,因此 dataclasses 能够检测到已为该参数传递了一个值(None 的值)。