Generic 和 TypeVar 的使用
Use of Generic and TypeVar
我无法理解 Generic
和 TypeVar
的用法以及它们之间的关系。
https://docs.python.org/3/library/typing.html#building-generic-types
文档有这个例子:
class Mapping(Generic[KT, VT]):
def __getitem__(self, key: KT) -> VT:
...
# Etc.
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
Type variables exist primarily for the benefit of static type
checkers. They serve as the parameters for generic types as well as
for generic function definitions.
我不能简单地将 Mapping
与某些现有类型一起使用,例如 int
,而不是创建 X
和 Y
?
使用 Generic
和 TypeVar
(此处表示为 X
和 Y
变量)的全部目的是希望参数为 generic 尽可能。在这种情况下,可以使用 int
。不同之处在于:静态分析器会将参数解释为始终是 int
.
使用泛型意味着函数接受任何类型的参数。静态分析器,例如在 IDE 中,将确定变量的类型和 return 类型,因为参数是在函数调用或对象实例化时提供的。
mapping: Mapping[str, int] = {"2": 2, "3": 3}
name = lookup_name(mapping, "1", 1)
在上面的示例中,类型检查器会知道 name
将始终是 int
,这取决于类型注释。在 IDE 秒中,int
方法的代码完成将显示为使用 'name' 变量。
如果这是您的目标,使用特定类型是理想的选择。该函数仅接受具有 int
键或值的映射,例如 and/or returning int
因为X
和Y
是可变的,基本上你可以选择任何想要的名字。
下面的例子是可能的:
def lookup_name(mapping: Mapping[str, int], key: str, default: int) -> int:
try:
return mapping[key]
except KeyError:
return default
上述示例中的类型不是通用的。密钥将始终是 str
;默认变量、值和 return 类型将始终是 int
。这是程序员的选择。但是,Python 并未强制执行此操作。为此需要像 mypy 这样的静态类型检查器。
如果需要,甚至可以限制通用类型:
import typing
X = typing.TypeVar("X", int, str) # Accept int and str
Y = typing.TypeVar("Y", int) # Accept only int
@MisterMiyagi 的回答对 TypeVar
和 Generic
.
的使用范围进行了详尽的解释
类型变量的字面意思是“类型的变量”。类似于常规变量允许代码应用于多个值,类型变量允许代码应用于多个类型.
同时,就像代码不需要应用于多个值一样,也不需要需要依赖多个类型。可以使用文字值代替变量,可以使用文字类型代替类型变量——前提是这些是唯一 values/types 适用的。
由于 Python 语言在语义上只知道 值 – 运行时类型也是值 – 它没有表达类型可变性的工具。即,它不能 define、reference 或 scope 类型变量。因此,typing
通过具体的事物表示这两个概念:
- A
typing.TypeVar
表示 定义 和 引用 类型变量。
- A
typing.Generic
表示类型的 范围 ,特别是 class 范围。
值得注意的是,可以在没有 Generic
的情况下使用 TypeVar
– 函数是自然范围的 – 而 Generic
没有 TypeVar
– 范围可以使用文字类型。
考虑一个添加两个东西的函数。最天真的实现添加了两个 literal 东西:
def add():
return 5 + 12
这是有效的,但不必要地受到限制。有人想 参数化 要添加的两件事——这就是常规变量的用途:
def add(a, b):
return a + b
现在考虑添加两个 typed 事物的函数。最天真的实现添加了两个 literal 类型的东西:
def add(a: int, b: int) -> int:
return a + b
这是有效的,但不必要地受到限制。有人想参数化要添加的两件事的类型——这就是类型变量的用途:
T = TypeVar("T")
def add(a: T, b: T) -> T:
return a + b
现在,对于值,我们定义了 两个 变量——a
和 b
但对于类型,我们定义了 one 变量 – 单个 T
– 但用于两个变量!就像表达式 a + a
意味着两个操作数是相同的值一样,注解 a: T, b: T
意味着两个参数是相同的类型。这是因为我们的函数在类型之间有很强的关系,但在值之间没有。
虽然类型变量在函数中的作用域是自动的——到函数作用域——但对于 classes 不是这种情况:类型变量的作用域可能是跨越所有methods/attributes 的 class 或 特定的 到某些 method/attribute.
当我们定义 class 时,我们可以通过将类型变量作为 参数 添加到 class 来将类型变量限定在 class 范围内.值得注意的是,参数始终是变量——这适用于常规参数,就像类型参数一样。参数化文字是没有意义的。
# v value parameters of the function are "value variables"
def mapping(keys, values):
...
# v type parameters of the class are "type variables"
class Mapping(Generic[KT, VT]):
...
当我们使用一个class时,它的参数范围已经定义好了。值得注意的是,传入的参数可以是文字或变量——这同样适用于常规参数,就像类型参数一样。
# v pass in arguments via literals
mapping([0, 1, 2, 3], ['zero', 'one', 'two', 'three'])
# v pass in arguments via variables
mapping(ks, vs)
# v pass in arguments via literals
m: Mapping[int, str]
# v pass in arguments via variables
m: Mapping[KT, VT]
是使用文字还是变量以及是否限定它们的范围取决于用例。但我们可以根据需要自由选择。
我无法理解 Generic
和 TypeVar
的用法以及它们之间的关系。
https://docs.python.org/3/library/typing.html#building-generic-types
文档有这个例子:
class Mapping(Generic[KT, VT]):
def __getitem__(self, key: KT) -> VT:
...
# Etc.
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
Type variables exist primarily for the benefit of static type checkers. They serve as the parameters for generic types as well as for generic function definitions.
我不能简单地将 Mapping
与某些现有类型一起使用,例如 int
,而不是创建 X
和 Y
?
使用 Generic
和 TypeVar
(此处表示为 X
和 Y
变量)的全部目的是希望参数为 generic 尽可能。在这种情况下,可以使用 int
。不同之处在于:静态分析器会将参数解释为始终是 int
.
使用泛型意味着函数接受任何类型的参数。静态分析器,例如在 IDE 中,将确定变量的类型和 return 类型,因为参数是在函数调用或对象实例化时提供的。
mapping: Mapping[str, int] = {"2": 2, "3": 3}
name = lookup_name(mapping, "1", 1)
在上面的示例中,类型检查器会知道 name
将始终是 int
,这取决于类型注释。在 IDE 秒中,int
方法的代码完成将显示为使用 'name' 变量。
如果这是您的目标,使用特定类型是理想的选择。该函数仅接受具有 int
键或值的映射,例如 and/or returning int
因为X
和Y
是可变的,基本上你可以选择任何想要的名字。
下面的例子是可能的:
def lookup_name(mapping: Mapping[str, int], key: str, default: int) -> int:
try:
return mapping[key]
except KeyError:
return default
上述示例中的类型不是通用的。密钥将始终是 str
;默认变量、值和 return 类型将始终是 int
。这是程序员的选择。但是,Python 并未强制执行此操作。为此需要像 mypy 这样的静态类型检查器。
如果需要,甚至可以限制通用类型:
import typing
X = typing.TypeVar("X", int, str) # Accept int and str
Y = typing.TypeVar("Y", int) # Accept only int
@MisterMiyagi 的回答对 TypeVar
和 Generic
.
类型变量的字面意思是“类型的变量”。类似于常规变量允许代码应用于多个值,类型变量允许代码应用于多个类型.
同时,就像代码不需要应用于多个值一样,也不需要需要依赖多个类型。可以使用文字值代替变量,可以使用文字类型代替类型变量——前提是这些是唯一 values/types 适用的。
由于 Python 语言在语义上只知道 值 – 运行时类型也是值 – 它没有表达类型可变性的工具。即,它不能 define、reference 或 scope 类型变量。因此,typing
通过具体的事物表示这两个概念:
- A
typing.TypeVar
表示 定义 和 引用 类型变量。 - A
typing.Generic
表示类型的 范围 ,特别是 class 范围。
值得注意的是,可以在没有 Generic
的情况下使用 TypeVar
– 函数是自然范围的 – 而 Generic
没有 TypeVar
– 范围可以使用文字类型。
考虑一个添加两个东西的函数。最天真的实现添加了两个 literal 东西:
def add():
return 5 + 12
这是有效的,但不必要地受到限制。有人想 参数化 要添加的两件事——这就是常规变量的用途:
def add(a, b):
return a + b
现在考虑添加两个 typed 事物的函数。最天真的实现添加了两个 literal 类型的东西:
def add(a: int, b: int) -> int:
return a + b
这是有效的,但不必要地受到限制。有人想参数化要添加的两件事的类型——这就是类型变量的用途:
T = TypeVar("T")
def add(a: T, b: T) -> T:
return a + b
现在,对于值,我们定义了 两个 变量——a
和 b
但对于类型,我们定义了 one 变量 – 单个 T
– 但用于两个变量!就像表达式 a + a
意味着两个操作数是相同的值一样,注解 a: T, b: T
意味着两个参数是相同的类型。这是因为我们的函数在类型之间有很强的关系,但在值之间没有。
虽然类型变量在函数中的作用域是自动的——到函数作用域——但对于 classes 不是这种情况:类型变量的作用域可能是跨越所有methods/attributes 的 class 或 特定的 到某些 method/attribute.
当我们定义 class 时,我们可以通过将类型变量作为 参数 添加到 class 来将类型变量限定在 class 范围内.值得注意的是,参数始终是变量——这适用于常规参数,就像类型参数一样。参数化文字是没有意义的。
# v value parameters of the function are "value variables"
def mapping(keys, values):
...
# v type parameters of the class are "type variables"
class Mapping(Generic[KT, VT]):
...
当我们使用一个class时,它的参数范围已经定义好了。值得注意的是,传入的参数可以是文字或变量——这同样适用于常规参数,就像类型参数一样。
# v pass in arguments via literals
mapping([0, 1, 2, 3], ['zero', 'one', 'two', 'three'])
# v pass in arguments via variables
mapping(ks, vs)
# v pass in arguments via literals
m: Mapping[int, str]
# v pass in arguments via variables
m: Mapping[KT, VT]
是使用文字还是变量以及是否限定它们的范围取决于用例。但我们可以根据需要自由选择。