以标记值作为默认值的类型提示参数

Type-hinting parameters with a sentinel value as the default

当我无法在函数签名中分配默认参数时,我目前使用 this strategy and/or None 已经有意义了。

from typing import Optional

DEFAULT = object()


# `None` already has meaning.
def spam(ham: Optional[list[str]] = DEFAULT):
    if ham is DEFAULT:
        ham = ['prosciutto', 'jamon']
    if ham is None:
        print('Eggs?')
    else:
        print(str(len(ham)) + ' ham(s).')

错误:

Failed (exit code: 1) (2607 ms)

main.py:7: error: Incompatible default for argument "ham" (default has type "object", argument has type "Optional[List[str]]")
Found 1 error in 1 file (checked 1 source file)

正如我评论的那样,这是 Python 中一个活跃的开发领域。 PEP 661 建议添加一个 sentinel 函数来创建哨兵对象,但在该 PEP 获得批准之前,您只能靠自己了。

不过,您可以从 PEP 中的一些提议(或拒绝)选项中获得灵感。一种与类型提示配合得相当好的非常简单的方法是让你的标记值成为 class:

class DEFAULT: pass

现在您可以对函数进行类型提示,将其作为包含 type[DEFAULT]:

的类型联合
def spam(ham: list[str]|None|type[DEFAULT] = DEFAULT):

我喜欢做的事情——这只是 的一个细微变化——是使用 metaclass 给我的 sentinel class 一个更好的 repr 并让它始终-假。


sentinel.py

from typing import Literal 

class SentinelMeta(type):
    def __repr__(cls) -> str:
        return f'<{cls.__name__}>'

    def __bool__(cls) -> Literal[False]:
        return False


class Sentinel(metaclass=SentinelMeta): pass

main.py

from sentinel import Sentinel

class DEFAULT(Sentinel): pass

您在类型提示中使用它的方式与@Blckknght 建议的完全相同:

def spam(ham: list[str]|None|type[DEFAULT] = DEFAULT): ...

但是你有额外的优势,你的哨兵值总是假的并且有更好的表现:

>>> DEFAULT
<DEFAULT>
>>> bool(DEFAULT)
False