【问题标题】:How do I declare return type based on argument type?如何根据参数类型声明返回类型?
【发布时间】:2021-07-17 00:22:32
【问题描述】:

这个问题与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")

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

【问题讨论】:

  • t 是指dict/str 类型的对象,还是字面意义上的类型本身?即你打电话给fun({}) 还是fun(dict)
  • @0x5453 好问题。我更新了我的问题以澄清。
  • 另外,什么版本的 Python?在 3.9 中,它们允许使用内置类型作为容器的类型提示(例如,您可以使用 list[int] 而不是 typing.List[int]),所以我想知道这对您所看到的内容是否有任何影响。
  • @0x5453 我现在卡在 3.8 上。

标签: python python-typing


【解决方案1】:

问题是我不明白它为什么起作用

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

我不明白为什么其他人不这样做。

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

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

【讨论】:

  • 关于第一个版本,我不确定您所说的绑定是什么意思。至于第二个,你的意思是if t == dict: return t({"foo": "bar"})返回一个Type类的实例吗?
  • @BigMcLargeHuge 更新了答案以进一步解释。
  • 实际上,Type[T] 必须适用于 any 类型 T,但并非所有类型在调用时都具有相同的签名。 mypy 正在查看 t 在正文中的第一次使用,并假设 tall 使用必须是这样的。 Callable[..., T] 通过接受任何可调用来削弱限制,无论其参数需要是什么类型。与其说它知道dict 采用一种参数,但str 采用另一种参数,不如mypy 根本不关心参数类型是什么。
【解决方案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 确实返回作为参数传递的类型的值。

【讨论】:

  • @AmiTavory 你觉得呢?重载很简单,但你仍然留下fun(t: Callable[..., T]) -> T,这对我来说是令人困惑的部分。无论如何,这并不能真正回答我的问题,但却是一个不错的选择。
猜你喜欢
  • 1970-01-01
  • 2020-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-21
相关资源
最近更新 更多