Python类型提示中type和Type的区别是什么?

5
今天,我遇到了一个用type进行类型提示的函数。
我已经做了一些研究,想知道何时应该使用typeType进行类型提示,但我找不到令人满意的答案。从我的研究中,似乎这两者之间存在一些重叠。
我的问题是:
  • typeType之间有什么区别?
  • 什么是示例用例,展示了何时使用typeType

研究

查看typing标记3.7.4.3的源代码Type(来自typing,我可以看到这个:

# 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识别出了一个差异。

2个回答

5

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

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

它们有几种关联方式。

  1. 当将type应用于参数时,注释的返回类型是Type。这与将list应用于参数(如list((1, 2)))的返回类型为List相同。在使用reveal_type时
reveal_type(type(1))

我们想知道当给定1时,type的返回值的推断类型注释是什么。答案是Type,更具体地说是Type [Literal [1]]
  1. Type是一个类型检查时间结构,type是一个运行时结构。这有各种含义,我稍后会解释。

接下来看您的示例,在:

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

我们不将extra注释为type,而是将关键字参数extra与值type一起传递给Type的元类。有关此构造的更多示例,请参见类级别关键字参数。请注意,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 成功地进行了类型检查。你可以看到,来自 naivev4 是错误的正例(false positive),因为 1 的类型是 int 而不是 str。但由于无法对 type 元类进行参数化(它不是 Generic),我们无法获得与 smart 相同的安全性。

我认为这更多是一种语言限制。你可以看到PEP 585尝试弥补相同类型的差距,但是针对的是列表 / List。最终,想法仍然是相同的:小写字母版本是运行时类,大写字母版本是类型注释。两者都可能重叠,但都具有独特的功能。


哇,这个答案让我学到了很多东西。谢谢 @MarioIshac!如果你继续发布这样的答案,我觉得你很快就会有很高的声望。 - Intrastellar Explorer

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接