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

Posted

技术标签:

【中文标题】类型和类型的Python类型提示之间的区别?【英文标题】:Difference between Python type hints of type and Type? 【发布时间】:2021-01-12 21:02:47 【问题描述】:

今天,我遇到了一个以type 提示的函数类型。

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

我的问题:

typeType 有什么区别? 什么是显示何时使用typeType 的示例用例?

研究

查看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.5mypy==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 认识到了差异。

【问题讨论】:

【参考方案1】:

type is a metaclass. 就像对象实例是类的实例一样,类也是元类的实例。

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,而是将值为type 的关键字参数extra 传递给Type 的元类。有关此构造的更多示例,请参阅Class-level Keyword Arguments。请注意,extra=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.

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

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

【讨论】:

哇,这个答案让我学到了很多东西。谢谢@MarioIshac!如果您继续发布这样的答案,我认为您很快就会拥有大量声誉

以上是关于类型和类型的Python类型提示之间的区别?的主要内容,如果未能解决你的问题,请参考以下文章

[python基础] python 2与python 3之间的区别 —— 不同数据类型间的运算

Python元组和序列区别是啥?

python与c区别都有哪些

类型方法和类型实例方法等之间的区别?

类型方法和类型实例方法等之间的区别?

03_python的数据类型