当参数默认值由模块级别变量确定时,构成超载函数

发布于 2025-02-03 09:40:46 字数 1071 浏览 2 评论 0原文

我具有带有输入参数的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(),没有参数无法正常工作的参数,因此会出现mypy错误。这里有更好的方法在这里提供类型信息吗?

I have a Python function with an input parameter, the value of which controls the type of the return value. The parameter may be omitted (default value None), in which case a module level variable is used. This way, the default behavior can be changed by changing the module level variable.
A minimal example:

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")

Normally one would use typing.overload to deal with this sort of situation. However, for test() without arguments that doesn't work--hence the mypy errors. Is there a better way to provide type info here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

面如桃花 2025-02-10 09:40:46

那是不可能的。有 github上的建议 添加typescript的typef ,但这是另一回事 - 它不会像您显示的那样跟踪可变变量。

我认为即使在理论上也无法实施这样的功能。它为类型检查带来了许多新的复杂性。我可以想到一些有问题的方案:

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

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

That's not possible. There is a proposal on GitHub to add typeof from TypeScript, but that's a different thing - it doesn't track mutable variables like you've shown.

I don't think such a feature can be implemented, even in theory. It brings a lot of new complexity into type checking. I can think of some problematic scenarios:

  1. Passing a callback
def foo(callback: Callable[[], int]):
    some_module.definitely_returns_an_int = callback

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

Now foo has saved a function that returns a string.

  1. Tracking mutations that happen inside functions
def ints_are_the_bomb():
    global option_default
    option_default = "int"

option_default = "str"
some_other_module.baz()

value = test()

Can you be sure about the type of value? There's no guarantee that some_other_module.baz() didn't call your_module.ints_are_the_bomb(). So now you need to track all changes that can ever happen to your_module.option_default, potentially across modules. And if client code (if this is a library) can change the flag, then it's just impossible.

To generalize, the type of a value (including functions) can't change when you mutate something. That could break distant code that also happens to have a reference to this object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文