如何根据参数类型声明返回类型?

时间:2021-07-15 18:55:57

标签: python python-typing

这个问题与 Python Typing: declare return value type based on function argument 类似,但不同之处在于它不适合放在评论中。

我有以下功能:

T = TypeVar('T', dict, str)

def fun(t: T) -> T:
    if t == dict:
        return {"foo": "bar"}
    else:
        return "foo"

我希望能够这样称呼它:

a_string = fun(str)
a_dict = fun(dict)

Pylance 在第二行抛出这个错误:

Expression of type "dict[str, str]" cannot be assigned to return type "T@fun"

最后一行出现这个错误:

Expression of type "Literal['foo']" cannot be assigned to return type "T@fun"

根据this answer,我应该能够做这样的事情:

T = TypeVar('T', dict, str)

def fun(t: Type[T]) -> T:
    if t == dict:
        return t({"foo": "bar"})
    else:
        return t("foo")

这会消除第二行的错误,但会导致最后一行出现不同的错误:

No overloads for "__init__" match the provided arguments
  Argument types: (Literal['foo'])

我研究了 this answer 很长时间才终于能够让它发挥作用:

T = TypeVar('T', dict, str)

def fun(t: Callable[..., T]) -> T:
    if t == dict:
        return t({"foo": "bar"})
    else:
        return t("foo")

这个问题是我不明白为什么它有效。我不明白为什么其他人不这样做。

2 个答案:

答案 0 :(得分:2)

<块引用>

问题在于我不明白它为什么有效

最后一个有效,因为类型可调用的。所以这里的输入是说 fun 接受一些东西,给定一些东西,将返回一个 T,你以类型的形式提供它。

<块引用>

我不明白为什么其他人不明白。

第一个版本不起作用,因为 -> 的左侧和右侧之间没有绑定。所以你可以传递一个类型变量,但你不能指定左边的特定必须是右边的特定。换句话说,如果参数是dict,那么根据签名,返回类型不一定是dict,而是typevar指定的类型(有点混乱,因为相同的符号 - T - 出现在两者中。但是,T 在这里表示“...之一”)。

第二个版本不起作用,因为 return 表示您也在返回类型,就类型检查器而言。

答案 1 :(得分:2)

您也可以在此处使用 typing.overload

from typing import TypeVar, Type, overload, Callable


T = TypeVar('T')


@overload
def fun(t: Type[dict]) -> dict:
    ...


@overload
def fun(t: Type[str]) -> str:
    ...


def fun(t: Callable[..., T]) -> T
    if t == dict:
        return t({"foo": "bar"})
    else:
        return t("foo")

这应该允许 fun(dict)fun(str),但不允许使用不同类型的调用,同时还确保 fun 确实返回作为参数传递的类型的值。