当我无法在函数签名中分配默认参数和/或 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)
ham
而不会在 mypy 中出现错误? 或DEFAULT = object()
?正如我所评论的,这是 Python 的一个活跃的开发领域。 PEP 661 建议添加一个
sentinel
函数来创建哨兵对象,但在该 PEP 获得批准之前,您只能靠自己了。
不过,您可以从 PEP 中提出的(或拒绝的)一些选项中获得灵感。一种非常简单的方法,可以很好地配合类型提示,就是让你的哨兵值成为一个类:
class DEFAULT: pass
现在您可以对函数进行类型提示,将其作为包含
type[DEFAULT]
的类型的并集:
def spam(ham: list[str]|None|type[DEFAULT] = DEFAULT):
我喜欢做的事情 - 这只是 @Blckknght 的答案的一个微小变化 - 是使用元类为我的哨兵类提供更好的表示并使其始终错误。
哨兵.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
现有答案没有任何问题,但我想说另一个有效的选择是使用
Enum
。事实上,这是PEP484 - 支持联合中的单例类型中推荐的方法:
为了在这种情况下进行精确键入,用户应将
类型与标准库提供的Union
类结合使用,以便可以静态捕获类型错误。enum.Enum
您的代码应该如下所示:
from enum import Enum
class Default(Enum):
token = 0
DEFAULT = Default.token
def spam(ham: list[str] | None | Default = DEFAULT):
if ham is DEFAULT:
ham = ["prosciutto", "jamon"]
if ham is None:
print("Eggs?")
else:
print(str(len(ham)) + " ham(s).")