当参数default由模块级变量决定时,提示重载函数的类型



我有一个带有输入参数的Python函数,其值控制返回值的类型。该参数可以省略(默认值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(),这不起作用-因此我的错误。这里是否有更好的方式来提供类型信息?

那不可能。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已经保存了一个返回字符串的函数。

  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上的所有更改,可能是跨模块的更改。如果客户端代码(如果这是一个库)可以改变标志,那么这是不可能的。

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

最新更新