Generic 和 TypeVar 的使用

Abh*_*kar 20 python type-hinting python-typing

我无法理解Genericand的用法TypeVar以及它们之间的关系。 https://docs.python.org/3/library/typing.html#building-generic-types

文档有这个例子:

class Mapping(Generic[KT, VT]):
    def __getitem__(self, key: KT) -> VT:
        ...
        # Etc.
Run Code Online (Sandbox Code Playgroud)
X = TypeVar('X')
Y = TypeVar('Y')

def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y:
    try:
        return mapping[key]
    except KeyError:
        return default
Run Code Online (Sandbox Code Playgroud)

类型变量的存在主要是为了静态类型检查器的利益。它们用作泛型类型以及泛型函数定义的参数。

为什么我不能简单地使用Mapping某些现有类型,例如int, 而不是创建Xand Y

Mis*_*agi 33

类型变量字面意思是“类型的变量”。与常规变量允许代码应用于多个类似,类型变量允许代码应用于多个类型
\n同时,就像代码不需要应用于多个值一样,它不需要依赖于多个类型。可以使用文字值代替变量,并且可以使用文字类型代替类型变量 \xe2\x80\x93 ,前提是这些是唯一适用的值/类型。

\n

由于 Python 语言在语义上只知道\xe2\x80\x93 运行时类型也是值 \xe2\x80\x93 它不具备表达类型可变性的设施。即,它不能定义引用作用域类型变量。因此,通过具体的事物来typing 表示这两个概念:

\n
    \n
  • Atyping.TypeVar表示类型变量的定义引用。
  • \n
  • Atyping.Generic代表类型的作用域,特别是类作用域。
  • \n
\n

值得注意的是,可以TypeVar在没有Generic\xe2\x80\x93 的情况下使用自然作用域为 \xe2\x80\x93 的函数,并且Generic没有TypeVar\xe2\x80\x93 作用域可以使用文字类型。

\n
\n

考虑一个将两件事相加的函数。最简单的实现添加了两个字面上的东西:

\n
def add():\n    return 5 + 12\n
Run Code Online (Sandbox Code Playgroud)\n

这是有效的,但没有必要受到限制。人们想要参数化这两件事以添加 \xe2\x80\x93 这就是常规变量的用途:

\n
def add(a, b):\n    return a + b\n
Run Code Online (Sandbox Code Playgroud)\n

现在考虑一个将两个类型的东西相加的函数。最简单的实现添加了两个文字类型的东西:

\n
def add(a: int, b: int) -> int:\n    return a + b\n
Run Code Online (Sandbox Code Playgroud)\n

这是有效的,但没有必要受到限制。想要参数化两个事物的类型以添加​​ \xe2\x80\x93 这就是类型变量的用途:

\n
T = TypeVar("T")\n\ndef add(a: T, b: T) -> T:\n    return a + b\n
Run Code Online (Sandbox Code Playgroud)\n

现在,在值的情况下,我们定义了两个变量 \xe2\x80\x93 ab但在类型的情况下,我们定义了一个变量 \xe2\x80\x93 ,即单个T\xe2\x80\x93 但用于两个变量!就像表达式a + a意味着两个操作数具有相同的值一样,注释a: T, b: T意味着两个参数具有相同的类型。这是因为我们的函数在类型之间有很强的关系,但在值之间没有。

\n
\n

虽然类型变量在函数 \xe2\x80\x93 中自动作用域为函数作用域 \xe2\x80\x93,但类的情况并非如此:类型变量的作用域可能跨类的所有方法/属性或特定于某些方法/属性方法/属性。

\n

当我们定义一个类时,我们可以通过将类型变量作为参数添加到类中来将其范围限制在类范围内。值得注意的是,参数始终是变量\xe2\x80\x93 这适用于常规参数,就像类型参数一样。参数化文字是没有意义的。

\n
#       v value parameters of the function are "value variables"\ndef mapping(keys, values):\n    ...\n\n#       v type parameters of the class are "type variables"\nclass Mapping(Generic[KT, VT]):\n    ...\n
Run Code Online (Sandbox Code Playgroud)\n

当我们使用一个类时,它的参数范围就已经被定义了。值得注意的是,传入的参数可以是文字或变量 \xe2\x80\x93 这再次适用于常规参数,就像类型参数一样。

\n
#       v pass in arguments via literals\nmapping([0, 1, 2, 3], [\'zero\', \'one\', \'two\', \'three\'])\n#       v pass in arguments via variables\nmapping(ks, vs)\n\n#          v pass in arguments via literals\nm: Mapping[int, str]\n#          v pass in arguments via variables\nm: Mapping[KT, VT]\n
Run Code Online (Sandbox Code Playgroud)\n

是否使用文字或变量以及是否限制它们的范围取决于用例。但我们可以根据需要自由选择。

\n