默认参数是否会覆盖 mypy 的类型提示?

问题描述 投票:0回答:2

以下代码如预期被

mypy
拒绝:

def foo(value: int) -> None:
    print(value, type(value))
foo(None)

输出:

error: Argument 1 to "foo" has incompatible type "None"; expected "int"

但是引入默认参数

None
后,就不再报错了:

def foo(value: int=None) -> None:
    print(value, type(value))
foo(None)

如果我们将

mypy
None
更改为
value
,我希望
int
只允许
Optional[int]
(作为参数和默认值),但似乎不需要这样做。为什么?

python nonetype mypy python-typing default-parameters
2个回答
16
投票

注意:对于 2021 年后的读者,请参阅本答案下方的答案,以获得目前更正确的答案。

当您使关键字参数接受

None
时,mypy 会隐式地将该参数设为
Optional[Blah]
类型(如果还不是)。您可以通过将
reveal_type(...)
函数添加到代码中并运行
mypy
:

来查看这一点
def foo(value: int = None) -> None:
    print(value, type(value))

reveal_type(foo)
foo(None)

输出将是:

test.py:4: error: Revealed type is 'def (value: Union[builtins.int, None] =)'

(请务必在实际运行代码之前删除

reveal_type
,因为该函数在运行时实际上并不存在——它只是 mypy 的特殊情况以帮助调试。)

这种行为的存在主要是因为它有助于减少函数签名的噪音。毕竟,如果

value
在某些时候被允许为 None,显然它必须同时接受整数和 None。在这种情况下,为什么不直接推断类型为
Optional[int]
(相当于
Union[int, None]
,顺便说一句),这样用户就不需要重复相同的信息两次?

当然,并不是每个人都喜欢这种行为:有些人更喜欢更加明确。在这种情况下,请使用

--no-implicit-optional
标志运行 mypy。这将产生以下输出:

test.py:1: error: Incompatible default for argument "value" (default has type "None", argument has type "int")
test.py:4: error: Revealed type is 'def (value: builtins.int =)'
test.py:5: error: Argument 1 to "foo" has incompatible type "None"; expected "int"

当然,您需要更改函数签名。

如果您想以各种其他方式提高 mypy 的严格性,请尝试传递

--strict
标志。这将自动启用
--no-implicit-optional
和其他几个严格性标志。有关更多详细信息,请运行
mypy --help


5
投票

为@Michael0x2a 的出色答案添加一些具有历史深度的参考。 签名参数上的默认值

None
应使其暗示的
type
隐式被视为
Optional[type]
的推荐推理规则最初是在 PEP 484 中建立的,但同时已发生变化。

工会类型 - PEP 484

此 PEP 的过去版本允许类型检查器在默认值为 None 时采用可选类型,如以下代码所示:

def handle_employee(e: Employee = None): ...

这将被视为等同于:

def handle_employee(e: Optional[Employee] = None) -> None: ...

这不再是推荐的行为。类型检查器应该朝着要求明确可选类型的方向发展。

如果我们查看 PEP 484 的修订历史,我们会看到 “GitHub 的责备”,这反过来又在 Pull 请求 #689 中给出了其推理,并引用回 打字问题 #275

© www.soinside.com 2019 - 2024. All rights reserved.