当参数默认值由模块级变量确定时,对重载函数进行类型提示

Type-hinting an overloaded function when parameter default is determined by module level variable

我有一个带有输入参数的 Python 函数,其值控制 return 值的类型。该参数可以省略(默认值 None),在这种情况下使用模块级变量。这样,可以通过更改模块级变量来更改默认行为。 一个最小的例子:

from typing import overload, Union, Literal, Optional

option_default: Literal["str", "int"] = "int"


@overload
def test(option: Literal["str"]) -> str:
    ...


@overload
def test(option: Literal["int"]) -> int:
    ...


@overload
def test(option: Literal[None] = None) -> Union[str, int]:
    ...


def test(option: Optional[Literal["str", "int"]] = None) -> Union[str, int]:

    if option is None:
        option = option_default

    if option == "str":
        return "foo"
    else:
        return 1


foo: int = test() # Incompatible types in assignment (expression has type "Union[str, int]", variable has type "int")

option_default = "str"
baz: str = test() # Incompatible types in assignment (expression has type "Union[str, int]", variable has type "str")

一般人会用typing.overload来应对这种情况。但是,对于 test() 没有不起作用的参数——因此出现 mypy 错误。有没有更好的方法在这里提供类型信息?

这不可能。 a proposal on GitHub 可以从 TypeScript 添加 typeof,但这是另一回事 - 它不会像您展示的那样跟踪可变变量。

我认为即使在理论上也无法实现这样的功能。它给类型检查带来了很多新的复杂性。我能想到一些有问题的场景:

  1. 传递回调
def foo(callback: Callable[[], int]):
    some_module.definitely_returns_an_int = callback

option_default = "int"
foo(test)  # ok?
option_default = "str"

现在foo已经保存了一个returns字符串的函数。

  1. 跟踪函数内部发生的突变
def ints_are_the_bomb():
    global option_default
    option_default = "int"

option_default = "str"
some_other_module.baz()

value = test()

你能确定value的类型吗?不能保证 some_other_module.baz() 没有调用 your_module.ints_are_the_bomb()。所以现在您需要跟踪 your_module.option_default 可能发生的所有变化,可能是跨模块的。如果客户端代码(如果这是一个库)可以更改标志,那么这是不可能的。

概括地说,值的类型(包括函数)在您改变某些东西时不会改变。这可能会破坏也恰好引用了该对象的远程代码。