首页 > 解决方案 > 类型和类型的Python类型提示之间的区别?

问题描述

今天,我遇到了一个用type.

我已经对何时应该使用typeor输入提示进行了一些研究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. ```

看起来Type可能只是 的别名type,但它支持Generic参数化。这个对吗?


例子

以下是使用Python==3.8.5and制作的一些示例代码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认识到差异。

标签: pythongenericstype-hintingpython-typing

解决方案


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

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

它们有几种关联方式。

  1. type应用于参数时带注释的返回类型是Type. 这与list应用于参数(如list((1, 2)))具有带注释的返回类型的方式相同List。在中使用reveal_type
reveal_type(type(1))

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

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

转到您的示例,在:

class Type(Generic[CT_co], extra=type):
    ...

我们没有注释extratype,而是将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.

v1v3并且v4类型检查成功。您可以看到v4fromnaive是误报,因为类型1int,而不是str。但是因为你不能参数化type元类(它不是Generic),所以我们无法获得我们所拥有的安全性smart

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


推荐阅读