0

考虑有一个父类和几个从该父类继承的子类的情况。当孩子被传递或返回时,设置一个TypeVar提示孩子的类型。为简单起见,只创建了一个孩子。

from typing import TypeVar


class Parent(object):
    pass

class Child(Parent):
    pass

T_co = TypeVar("T_co", bound=Parent)

mypy会根据应用类型提示的位置而做出不同的响应。当应用于 return 时,mypy会引发以下错误

def hint_return() -> T_co:
    return Child()
Incompatible return value type (got "Child", expected "T_co")mypy(error)

mypy但是,当适用于争论时,没有提出任何抱怨。

def hint_arg(child: T_co):
    pass

register_arg(Child())

为什么会出现这种差异?TypeVar用于返回类型提示的正确方法是什么?

4

1 回答 1

2

关于必须如何使用 TypeVars,基本上有两个隐含的规则:

  1. TypeVar 必须在参数类型提示中至少使用一次。
  2. TypeVar 必须在函数签名中至少出现两次。

如果你不遵循这两条规则,你要么最终构造一个函数类型签名,它实际上并没有做太多事情并且包含一个多余的 TypeVar,要么不可能对其执行类型推断。

hint_return的函数是违反规则 1 的函数的示例。这是有问题的原因是因为当 mypy 看到如下调用时:

x = hint_return()

...它试图仅x使用调用站点和类型签名中可用的信息来推断 of的类型——它不检查.hint_return

(但是如果 mypy 尝试使用预先存在的类型x作为提示呢?好吧,hint_return实际上不可能在运行时利用该信息,因此该信息不可能与类型推断相关。这又是一个规则 1 的反映: TypeVar 意味着在调用函数时被更具体的类型替换,这意味着您需要实际指定该特定类型作为输入。)

hint_arg的函数是违反规则 2 的函数示例。在这种情况下,您的 TypeVar 最终没有任何用途:将函数重写为:

def hint_arg_simplified(child: Parent):
    pass

毕竟,用传入的实际类型替换 T_co 没有任何意义。由于hint_arg仍然需要能够接受任何 Parent 的任意子类型,因此无论如何它们hint_arghint_arg_simplified必须完全一样。

(请记住,如果键入一个函数以接受 Parent,它实际上必须接受 Parent 和 Parent 的任何子类型。即 mypy 假设您的类型遵循 Liskov 替换原则并相应地执行类型检查)

但是做:

T = TypeVar('T', bound=Parent)

def two_args_v1(x: T, y: T) -> None: pass

...与做以下事情有很大不同:

def two_args_v2(x: Parent, y: Parent) -> None: pass

在前者中,我们知道 x 和 y 必须是完全相同的类型,而对于后者我们不知道。这是可以在类型推断期间使用的相关且新的信息。


关于泛型类的澄清说明。从表面上看,他们似乎违反了这些规则。例如,mypy 对下面的类定义非常满意,即使它似乎违反了这两个规则!为什么?

from typing import Generic, TypeVar

T = TypeVar('T')

class Wrapper(Generic[T]):
    # Violates rule 2?
    def __init__(self, x: T) -> None:
        self.x = x

    # Violates rule 1 and 2?
    def unwrap(self) -> T:
        return self.x

嗯,这是因为我们实际上并没有查看完整的类型签名。我们通常会省略 from 的类型self,但它们实际上都还在。一旦我们添加回自动推断的self类型,很明显这两个规则实际上都被遵循了:

class Wrapper(Generic[T]):
    def __init__(self: Wrapper[T], x: T) -> None:
        self.x = x

    def unwrap(self: Wrapper[T]) -> T:
        return self.x
于 2020-03-27T17:26:10.057 回答