Python 类型提示的 type 和 Type 的区别?

问题描述 投票:0回答:2

今天,我遇到了一个带有

type
提示的函数类型。

我已经做了一些关于何时应该使用

type
Type
进行提示的研究,但我找不到满意的答案。根据我的研究,两者之间似乎存在一些重叠。

我的问题:

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

研究

查看

Type
的来源(来自
typing
标签
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
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
认识到了差异。

python generics type-hinting python-typing
2个回答
3
投票

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=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
naive
是误报,因为
1
的类型是
int
,而不是
str
。但是因为您无法参数化
type
元类(它不是
Generic
),所以我们无法获得
smart
所具有的安全性。

我认为这更多的是语言限制。您可以看到 PEP 585 正在尝试弥合同样类型的差距,但针对的是

list
/
List
。但归根结底,想法仍然是一样的:小写版本是运行时类,大写版本是类型注释。两者可以重叠,但有两者独有的功能。


0
投票

自 Python 3.9 起,它们是相同的,并且

typing.Type
已弃用。引用Python文档

自 3.9 版起已弃用:builtins.type 现在支持下标 ([])。请参阅 PEP 585通用别名类型

Mario Ishac 的答案对于早期版本来说是正确的。

© www.soinside.com 2019 - 2024. All rights reserved.