当参数默认值由模块级变量确定时,对重载函数进行类型提示
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
,但这是另一回事 - 它不会像您展示的那样跟踪可变变量。
我认为即使在理论上也无法实现这样的功能。它给类型检查带来了很多新的复杂性。我能想到一些有问题的场景:
- 传递回调
def foo(callback: Callable[[], int]):
some_module.definitely_returns_an_int = callback
option_default = "int"
foo(test) # ok?
option_default = "str"
现在foo
已经保存了一个returns字符串的函数。
- 跟踪函数内部发生的突变
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
可能发生的所有变化,可能是跨模块的。如果客户端代码(如果这是一个库)可以更改标志,那么这是不可能的。
概括地说,值的类型(包括函数)在您改变某些东西时不会改变。这可能会破坏也恰好引用了该对象的远程代码。
我有一个带有输入参数的 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
,但这是另一回事 - 它不会像您展示的那样跟踪可变变量。
我认为即使在理论上也无法实现这样的功能。它给类型检查带来了很多新的复杂性。我能想到一些有问题的场景:
- 传递回调
def foo(callback: Callable[[], int]):
some_module.definitely_returns_an_int = callback
option_default = "int"
foo(test) # ok?
option_default = "str"
现在foo
已经保存了一个returns字符串的函数。
- 跟踪函数内部发生的突变
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
可能发生的所有变化,可能是跨模块的。如果客户端代码(如果这是一个库)可以更改标志,那么这是不可能的。
概括地说,值的类型(包括函数)在您改变某些东西时不会改变。这可能会破坏也恰好引用了该对象的远程代码。