Python type hints of type 和 Type 之间的区别?
Difference between Python type hints of type and Type?
今天,我遇到了一个用 type
提示的函数类型。
关于什么时候应该使用 type
或 Type
键入提示,我做了一些研究,但我找不到满意的答案。根据我的研究,两者之间似乎有一些重叠。
我的问题:
type
和Type
有什么区别?
- 显示何时使用
type
与 Type
的示例用例是什么?
研究
查看 Type
(from typing
tag 3.7.4.3
) 的源代码,我可以看到:
# Internal type variable used for Type[].
CT_co = TypeVar('CT_co', covariant=True, bound=type)
# This is not a real generic class. Don't use outside annotations.
class Type(Generic[CT_co], extra=type):
"""A special construct usable to annotate class objects. ```
看起来 Type
可能只是 type
的别名,除了它支持 Generic
参数化。这是正确的吗?
例子
这是一些使用 Python==3.8.5
和 mypy==0.782
制作的示例代码:
from typing import Type
def foo(val: type) -> None:
reveal_type(val) # mypy output: Revealed type is 'builtins.type'
def bar(val: Type) -> None:
reveal_type(val) # mypy output: Revealed type is 'Type[Any]'
class Baz:
pass
foo(type(bool))
foo(Baz)
foo(Baz()) # error: Argument 1 to "foo" has incompatible type "Baz"; expected "type"
bar(type(bool))
bar(Baz)
bar(Baz()) # error: Argument 1 to "bar" has incompatible type "Baz"; expected "Type[Any]"
显然 mypy
认识到了差异。
type
is a metaclass. 就像对象实例是 classes 的实例一样,classes 是 metaclasses.
的实例
Type
是一个注释,用于告诉类型检查器 class 对象本身将在使用注释的任何地方处理,而不是 class 的实例对象。
它们之间有几种关联方式。
- 当
type
应用于参数时,注释的 return 类型是 Type
。这与应用于参数(如 list((1, 2))
)的 list
具有带注释的 returned 类型 List
的方式相同。在 中使用
reveal_type(type(1))
我们问的是 type
的 return 值在给定 1 时的推断类型注释是什么。答案是 Type
,更具体地说是 Type[Literal[1]]
.
Type
一个 type-check-time 构造,type
是一个运行时构造。这有多种含义,我稍后会解释。
转到您的示例,在:
class Type(Generic[CT_co], extra=type):
...
我们不会将 extra
注释为 type
,而是将值 type
的 keyword-argument extra
传递给 metaclass Type
个。有关此构造的更多示例,请参阅 Class-level Keyword Arguments。请注意 extra=type
与 extra: type
非常不同:一个是在运行时赋值,一个是在 type-check 时使用类型提示进行注释.
现在是有趣的部分:如果 mypy
能够成功地对两者进行类型检查,为什么要使用一个而不是另一个?答案在于 Type
作为一个 type-check 时间构造,与打字生态系统的集成要好得多。给出这个例子:
from typing import Type, TypeVar
T = TypeVar("T")
def smart(t: Type[T], v: T) -> T:
return v
def naive(t: type, v: T) -> T:
return v
v1: int = smart(int, 1) # Success.
v2: int = smart(str, 1) # Error.
v3: int = naive(int, 1) # Success.
v4: int = naive(str, 1) # Success.
v1
、v3
和 v4
type-check 成功。您可以看到 naive
中的 v4
是误报,因为 1
的类型是 int
,而不是 str
。但是因为你不能参数化 type
metaclass(它不是 Generic
),我们无法获得 smart
.[=51= 的安全性。 ]
我认为这更多是语言限制。您可以看到 PEP 585 试图弥合相同类型的差距,但 list
/ List
。归根结底,想法仍然是一样的:小写版本是运行时 class,大写版本是类型注释。两者可以重叠,但有两者独有的功能。
今天,我遇到了一个用 type
提示的函数类型。
关于什么时候应该使用 type
或 Type
键入提示,我做了一些研究,但我找不到满意的答案。根据我的研究,两者之间似乎有一些重叠。
我的问题:
type
和Type
有什么区别?- 显示何时使用
type
与Type
的示例用例是什么?
研究
查看 Type
(from typing
tag 3.7.4.3
) 的源代码,我可以看到:
# Internal type variable used for Type[]. CT_co = TypeVar('CT_co', covariant=True, bound=type) # This is not a real generic class. Don't use outside annotations. class Type(Generic[CT_co], extra=type): """A special construct usable to annotate class objects. ```
看起来 Type
可能只是 type
的别名,除了它支持 Generic
参数化。这是正确的吗?
例子
这是一些使用 Python==3.8.5
和 mypy==0.782
制作的示例代码:
from typing import Type
def foo(val: type) -> None:
reveal_type(val) # mypy output: Revealed type is 'builtins.type'
def bar(val: Type) -> None:
reveal_type(val) # mypy output: Revealed type is 'Type[Any]'
class Baz:
pass
foo(type(bool))
foo(Baz)
foo(Baz()) # error: Argument 1 to "foo" has incompatible type "Baz"; expected "type"
bar(type(bool))
bar(Baz)
bar(Baz()) # error: Argument 1 to "bar" has incompatible type "Baz"; expected "Type[Any]"
显然 mypy
认识到了差异。
type
is a metaclass. 就像对象实例是 classes 的实例一样,classes 是 metaclasses.
Type
是一个注释,用于告诉类型检查器 class 对象本身将在使用注释的任何地方处理,而不是 class 的实例对象。
它们之间有几种关联方式。
- 当
type
应用于参数时,注释的 return 类型是Type
。这与应用于参数(如list((1, 2))
)的list
具有带注释的 returned 类型List
的方式相同。在 中使用
reveal_type(type(1))
我们问的是 type
的 return 值在给定 1 时的推断类型注释是什么。答案是 Type
,更具体地说是 Type[Literal[1]]
.
Type
一个 type-check-time 构造,type
是一个运行时构造。这有多种含义,我稍后会解释。
转到您的示例,在:
class Type(Generic[CT_co], extra=type):
...
我们不会将 extra
注释为 type
,而是将值 type
的 keyword-argument extra
传递给 metaclass Type
个。有关此构造的更多示例,请参阅 Class-level Keyword Arguments。请注意 extra=type
与 extra: type
非常不同:一个是在运行时赋值,一个是在 type-check 时使用类型提示进行注释.
现在是有趣的部分:如果 mypy
能够成功地对两者进行类型检查,为什么要使用一个而不是另一个?答案在于 Type
作为一个 type-check 时间构造,与打字生态系统的集成要好得多。给出这个例子:
from typing import Type, TypeVar
T = TypeVar("T")
def smart(t: Type[T], v: T) -> T:
return v
def naive(t: type, v: T) -> T:
return v
v1: int = smart(int, 1) # Success.
v2: int = smart(str, 1) # Error.
v3: int = naive(int, 1) # Success.
v4: int = naive(str, 1) # Success.
v1
、v3
和 v4
type-check 成功。您可以看到 naive
中的 v4
是误报,因为 1
的类型是 int
,而不是 str
。但是因为你不能参数化 type
metaclass(它不是 Generic
),我们无法获得 smart
.[=51= 的安全性。 ]
我认为这更多是语言限制。您可以看到 PEP 585 试图弥合相同类型的差距,但 list
/ List
。归根结底,想法仍然是一样的:小写版本是运行时 class,大写版本是类型注释。两者可以重叠,但有两者独有的功能。