首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >get_type_hints on TypeVar

get_type_hints on TypeVar
EN

Stack Overflow用户
提问于 2020-03-11 12:17:21
回答 1查看 367关注 0票数 1

给定这样的函数签名:

代码语言:javascript
复制
T = typing.TypeVar('T')

def foo(arg: T) -> '?':
  return arg.op()

函数的返回类型与arg类型的"op“方法的返回类型相同。在我看来,这个信息应该由IDE来还原。

代码语言:javascript
复制
class SomeClass:
  def op() -> int:
    ...

x = foo(SomeClass()) # IDE should know that x has type 'int'.

是否有类型语法暗示我可以从foo的函数签名中描述这个依赖项?换句话说,我应该用什么表达式来替换上面代码中的'?'以获得所需的效果?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-03-12 15:43:45

在这里使用TypeVar是行不通的。由于您的TypeVar是无约束的(不声明自己受到一个或多个类型的限制),所以可以用它来替代任意类型--对象、int、str、float、Dict[str,Liststr].

这意味着使用当前输入类型签名,返回类型是不可表达的:给定输入,有时只是没有有效的返回类型。

更广泛地说,您的工具是否能够推断返回类型实际上与此无关。PEP 484类型系统没有一种方法可以让您声明一个完全基于函数体中某些内容的返回类型。相反,您必须声明返回类型是固定的、具体的(如int或str),或者是以某种方式与输入相关的泛型类型。

后一种做法有两种不同的方式。

首先,您可以坚持使用op()方法的任何类都继承自某个类,并让该类使op的输出成为泛型。例如:

代码语言:javascript
复制
from typing import Generic, TypeVar

TOp = TypeVar('TOp')

class Operation(Generic[TOp]):
    def op(self) -> TOp: pass

class SomeClass(Operation[int]):
    def op(self) -> int:
        # ...snip...
        return 4

class OtherClass(Operation[str]):
    def op(self) -> str:
        # ...snip...
        return "output"

# Or alternatively, reuse the TOp variable from above -- it would mean the
# exact same thing, since TypeVars are only placeholders.
T = TypeVar('T')

def foo(arg: Operation[T]) -> T:
    return arg.op()

# Deduces the output types should be int and str respectively
a = foo(SomeClass())
b = foo(OtherClass())

反过来,将这些约束强加于arg,可以让我们得出返回类型必须是什么。

这种方法的唯一缺点是修改SomeClass和OtherClass可能有点麻烦。如果您不喜欢这样做,那么您可以做的是创建一个自定义协议

代码语言:javascript
复制
# Or, if you need to use older versions of Python, pip-install typing-extensions
# and do 'from typing_extensions import Protocol'.
from typing import Generic, Protocol, TypeVar

TOp = TypeVar('TOp')

# Doing 'class Blah(Protocol[T])' is a shorthand for
# doing 'class Blah(Protocol, Generic[T])'.
class SupportsOp(Protocol[TOp]):
    def op(self) -> TOp: ...

# SomeClass and OtherClass do *not* inherit SupportsOp!

class SomeClass:
    def op(self) -> int:
        # ...snip...
        return 4

class OtherClass:
    def op(self) -> str:
        # ...snip...
        return "output"

# Or alternatively, reuse the TOp variable from above -- it would mean the
# exact same thing, since TypeVars are only placeholders.
T = TypeVar('T')

def foo(arg: SupportsOp[T]) -> T:
    return arg.op()

# Still deduces the output types should be int and str respectively

a = foo(SomeClass())
b = foo(OtherClass())

主要的区别是协议使用结构子类型而不是标称子类型。对于标称子类型,A类型被认为是B的一个子类型,如果A是从B显式继承而来的,则A是B的一个子类型,只要它的方法符合B中给出的方法签名。

当然,IDE是否能够对这样的泛型执行类型推断是另一个问题。像mypy这样的类型检查程序肯定没有问题,而且像PyCharm这样的IDE通常大部分时间都做得很好,但是您可能需要做一些测试才能知道什么是支持的,哪些是不支持的。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60635858

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档