以哨兵值作为默认值的类型提示参数

zat*_*heo 10 python type-hinting mypy python-typing python-3.9

目前,当我无法在函数签名中分配默认参数和/或已经具有含义时,我会使用此策略。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).')
Run Code Online (Sandbox Code Playgroud)

错误:

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)
Run Code Online (Sandbox Code Playgroud)
  • 如何在 mypy 中输入提示ham而不出现错误?或者
  • 我应该使用什么策略来代替DEFAULT = object()

Blc*_*ght 12

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

不过,您可以从 PEP 中提出的(或拒绝的)一些选项中获得灵感。一种非常简单的方法,可以很好地使用类型提示,那就是让你的哨兵值成为一个类:

class DEFAULT: pass
Run Code Online (Sandbox Code Playgroud)

现在,您可以将函数类型提示为采用类型的联合,其中包括type[DEFAULT]

def spam(ham: list[str]|None|type[DEFAULT] = DEFAULT):
Run Code Online (Sandbox Code Playgroud)


Ale*_*ood 6

我喜欢做的事情 \xe2\x80\x94 这只是@Blckknght\'s 答案\xe2\x80\x94 的一个微小变化,那就是使用元类给我的哨兵类一个更好的表示并使其始终错误。

\n
\n

哨兵.py

\n
from typing import Literal \n\nclass SentinelMeta(type):\n    def __repr__(cls) -> str:\n        return f\'<{cls.__name__}>\'\n\n    def __bool__(cls) -> Literal[False]:\n        return False\n\n\nclass Sentinel(metaclass=SentinelMeta): pass\n
Run Code Online (Sandbox Code Playgroud)\n

主要.py

\n
from sentinel import Sentinel\n\nclass DEFAULT(Sentinel): pass\n
Run Code Online (Sandbox Code Playgroud)\n
\n

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

\n
def spam(ham: list[str]|None|type[DEFAULT] = DEFAULT): ...\n
Run Code Online (Sandbox Code Playgroud)\n

但你还有一个额外的优势,那就是你的哨兵值总是错误的,并且有更好的表现:

\n
>>> DEFAULT\n<DEFAULT>\n>>> bool(DEFAULT)\nFalse\n
Run Code Online (Sandbox Code Playgroud)\n