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

时间:2018-06-27 06:29:04

标签: python types python-3.6 mypy

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)

如果我们将mypyNone更改为value,我希望int仅允许Optional[int](作为参数和默认值),但是似乎不需要这个。为什么?

2 个答案:

答案 0 :(得分:2)

当您使关键字参数接受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,那么显然它必须同时接受int和None。在那种情况下,为什么不仅仅推断类型为Optional[None](等于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

答案 1 :(得分:-1)

在@Michael0x2a 的优秀答案中添加一些具有历史深度的参考资料。 推荐推断规则,即签名参数的默认值 None 应使其暗示的 type 隐式被视为 Optional[type] 最初在 PEP 484 中建立,但同时已更改。< /p> <块引用>

Union types - PEP 484

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

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

这将被视为等同于:

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

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

如果我们查看 PEP 484's revision history,我们会到达 "GitHub's blame",它反过来在 Pull request #689 中给出其推理并引用回 typing issue #275