python - 类型和类型的Python类型提示之间的区别?
问题描述
今天,我遇到了一个用type
.
我已经对何时应该使用type
or输入提示进行了一些研究Type
,但我找不到令人满意的答案。根据我的研究,两者之间似乎存在一些重叠。
我的问题:
type
和 和有什么不一样Type
?- 什么是显示何时使用
type
vs的示例用例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
and制作的一些示例代码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
是一个元类。就像对象实例是类的实例一样,类也是元类的实例。
Type
是一个注解,用于告诉类型检查器类对象本身将在使用注解的任何位置处理,而不是该类对象的实例。
它们有几种关联方式。
type
应用于参数时带注释的返回类型是Type
. 这与list
应用于参数(如list((1, 2))
)具有带注释的返回类型的方式相同List
。在中使用reveal_type:
reveal_type(type(1))
我们在问什么是推断的类型注释,type
当它被给定 1 时的返回值。答案是Type
,更具体地说Type[Literal[1]]
。
Type
类型检查时间构造type
是运行时构造。这有各种含义,我稍后会解释。
转到您的示例,在:
class Type(Generic[CT_co], extra=type):
...
我们没有注释extra
为type
,而是将extra
带有值的关键字参数传递type
给 的元类Type
。有关此构造的更多示例,请参阅类级关键字参数。请注意,这extra=type
与非常不同:extra: type
一种是在运行时分配值,另一种是在类型检查时使用类型提示进行注释。
现在来看有趣的部分:如果mypy
能够对两者都进行成功的类型检查,为什么要使用一个而不是另一个?答案在于Type
,作为类型检查时间构造,它与类型生态系统的集成度更高。给定这个例子:
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
类型检查成功。您可以看到v4
fromnaive
是误报,因为类型1
是int
,而不是str
。但是因为你不能参数化type
元类(它不是Generic
),所以我们无法获得我们所拥有的安全性smart
。
我认为这更多的是语言限制。您可以看到PEP 585试图弥合相同类型的差距,但用于list
/ List
。归根结底,想法还是一样:小写版本是运行时类,大写版本是类型注释。两者都可以重叠,但两者都有独有的功能。
推荐阅读
- python - Matplotlib 透明点在透明线上
- javascript - Node.js,MongoDB 错误 - “消息”:“模式尚未注册模型 \“类别”。\n使用 mongoose.model(名称,模式)”
- c++ - 减少已经使用 Git 的编译时间
- yocto - 基于 Yocto 项目的发行版中的 setxkbmap 等效项
- javascript - 当我使用 useChange 从数字键盘设置值时,挂钩调用无效
- python - 如何使用 if-else 语句在多个列表中插入相等数量的值?
- java - Scala枚举映射实现
- mysql - 如何从数据库连接表中查询并使用日期或范围之间?
- javascript - Chart.js update() 方法最终只工作一次,而不是每次迭代都更新
- amazon-web-services - 使用 VPN 网关在本地将 AWS EC2 连接到 Google Cloud SQL 实例