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

问题描述

今天,我遇到了一个用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认识到差异。

解决方法

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

@echo off setlocal set DATA=%DATE:~10,4%%DATE:~4,2%%DATE:~7,2%_%TIME:~0,2%h%TIME:~3,2%m set HOUR=%TIME:~0,2% if "%HOUR:~0,1%" == " " set DATA=%DATE:~10,2%_%TIME:~1,1%h%TIME:~3,2%m @Set "LIST=serversRDPok.txt" @Set "USER=%USERDOMAIN%\%USERNAME%" @Set "PASSWORD=password123" @Set "RESULTS=SQLserverVersion_MR_PRD_%DATA%.csv" @"%__APPDIR__%wbem\WMIC.exe" /FailFast:On /Node:"@%LIST%" /Output:"%RESULTS%" /Password:"%PASSWORD%" /User:"%USER%" process call create "sqlcmd -? | find "Version"" /Format:CSV endlocal pause 是一个注释,用于告知类型检查器,无论使用该注释的位置,都是在处理该类对象本身,而不是该类对象的实例。

它们之间有几种关联方式。

  1. Type应用于自变量时,带注释的返回类型为type。这与应用于参数(例如Type)的list具有带注释的返回类型list((1,2))相同。在以下位置使用reveal_type
List

我们要问的是reveal_type(type(1)) 返回值为1时推断出的类型注释是什么。答案是type,更具体地说是Type

  1. Type[Literal[1]]是类型检查时间构造,Type是运行时构造。这具有多种含义,我将在后面解释。

进入示例,

type

我们没有将class Type(Generic[CT_co],extra=type): ... 注释为extra,而是将具有值type的关键字参数extra传递给type的元类。有关此构造的更多示例,请参见Class-level Keyword Arguments。请注意,Typeextra=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中的v4是误报,因为naive的类型是1,而不是int。但是因为您无法参数化str元类(它不是type),所以我们无法获得Generic所具有的安全性。

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...