Python 类型提示和 Type 之间的区别?

Int*_*rer 6 python generics type-hinting python-typing

今天,我遇到了一个用 暗示的函数类型type

我已经做了一些关于何时应该使用type或键入提示的研究Type,但我找不到满意的答案。根据我的研究,两者之间似乎存在一些重叠。

我的问题:

  • type和 和有什么区别Type
  • type显示何时使用vs 的示例用例是什么Type

研究

Type查看(from typingtag 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. ```
Run Code Online (Sandbox Code Playgroud)

它看起来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]"
Run Code Online (Sandbox Code Playgroud)

清楚地mypy认识到差异。

Mar*_*hac 5

type是一个元类。就像对象实例是类的实例一样,类是元类的实例。

Type是一个注释,用于告诉类型检查器在使用该注释的任何地方都将处理类对象本身,而不是该类对象的实例。

它们有几种关联方式。

  1. type应用于参数时带注释的返回类型是Type。这与应用于具有带注释的返回类型的list参数(如)的方式相同。在以下位置使用reveal_typelist((1, 2))List
reveal_type(type(1))
Run Code Online (Sandbox Code Playgroud)

我们问的是,当给定 1 时,返回值的推断类型注释是什么。更具体地说,type答案是。TypeType[Literal[1]]

  1. Type类型检查时构造type是运行时构造。这有多种含义,我将在稍后解释。

继续你的例子,在:

class Type(Generic[CT_co], extra=type):
    ...
Run Code Online (Sandbox Code Playgroud)

我们没有注释extra为,而是将带有值的type关键字参数传递给 的元类。有关此构造的更多示例,请参阅类级关键字参数。请注意,这与非常不同:一种是在运行时赋值,另一种是在类型检查时使用类型提示进行注释。extratypeTypeextra=typeextra: 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.
Run Code Online (Sandbox Code Playgroud)

v1v3并且v4类型检查成功。您可以看到,鉴于is 、 not的类型, v4fromnaive是误报。但是因为您无法参数化元类(它不是),所以我们无法获得.1intstrtypeGenericsmart

我认为这更多的是语言限制。您可以看到PEP 585正在尝试弥合同样的差距,但对于list/ List。但归根结底,想法仍然是一样的:小写版本是运行时类,大写版本是类型注释。两者可以重叠,但也有两者独有的功能。