什么是Python 3.5中的类型提示

Vau*_*ein 216 python type-hinting python-3.x python-3.5

__CODE__据说其中一个被谈到的功能__CODE__.

的例子__CODE__本中提到的文章同时也提到使用类型负责任提示.有人可以解释更多关于它以及什么时候应该使用它什么时候不使用?

Jim*_*ard 290

我建议阅读PEP 483PEP 484和观看演示由Guido的类型提示.

简而言之:类型提示实际上就是单词的意思,你暗示了你正在使用的对象的类型.

由于Python 的动态特性,推断或检查正在使用的对象的类型尤其困难.这一事实使得开发人员很难理解他们未编写的代码到底发生了什么,最重要的是,许多IDE中的类型检查工具[PyCharm,PyDev浮现在脑海中]由于以下事实而受到限制:它们没有任何关于物体类型的指示.因此,他们试图推断出类型(如演示文稿中所述)大约50%的成功率.


从类型提示演示中获取两个重要的幻灯片:

为什么要输入提示?

  1. 帮助类型检查器:通过暗示您希望对象成为什么类型,类型检查器可以轻松检测您是否正在传递具有不期望类型的对象.
  2. 帮助提供文档:第三个查看代码的人将知道在什么地方,ergo,如何在不获取它们的情况下使用它TypeErrors.
  3. 帮助IDE开发更准确,更强大的工具:在了解对象的类型时,开发环境更适合建议适当的方法.您可能已经在某些时候使用某些IDE遇到过这种情况,点击.并且弹出了没有为对象定义的方法/属性.

为什么要使用Static Type Checkers?

  • 更快发现错误:我相信这是不言而喻的.
  • 项目越大,您需要的就越多:再次,有道理.静态语言提供动态语言缺乏的稳健性和控制性.您的应用程序越大越复杂,您需要的控制和可预测性(从行为方面)就越多.
  • 大型团队已经开始运行静态分析:我猜测这会验证前两点.

作为这个小介绍的结束语:这是一个可选功能,据我所知,它是为了获得静态类型的一些好处而引入的.

你一般不会需要担心它,绝对不需要使用它(尤其是在你使用Python作为辅助脚本语言的情况下).在开发大型项目时应该会有所帮助,因为它提供了非常需要的稳健性,控制和额外的调试功能.


使用mypy键入提示:

为了使这个答案更加完整,我认为一点点演示是合适的.我将使用mypy,这个图书馆启发了类型提示,因为它们在PEP中呈现.这主要是针对任何碰到这个问题并想知道从哪里开始的人写的.

在此之前,请允许我重申以下内容:PEP 484不执行任何操作; 它只是为功能注释设置方向,并为如何 /应该执行类型检查提出指导.您可以注释您的功能并提示您想要的任意数量; 无论是否存在注释,您的脚本仍将运行,因为Python本身不使用它们.

无论如何,如PEP所述,提示类型通常应采用三种形式:

此外,您还需要将类型提示与typing引入的新模块结合使用Py3.5.在其中,定义了许多(附加的)ABC(抽象基类)以及用于静态检查的辅助函数和装饰器.大多数ABCscollections.abc被包括在内,但在一个Generic形式,以允许订阅(通过定义一个__getitem__()方法).

对于任何对这些内容有更深入解释感兴趣的人来说,mypy documentation编写得非常好,并且有很多代码示例演示/描述了他们的检查器的功能; 绝对值得一读.

功能注释和特殊注释:

首先,观察使用特殊注释时可以获得的一些行为很有意思.# type: type如果无法直接推断,则可以在变量赋值期间添加特殊注释以指示对象的类型.简单的分配通常很容易推断,但其他的,如列表(关于其内容),则不能.

注意:如果我们想要使用任何衍生产品Containers并且需要指定该容器的内容,我们必须使用模块中的泛型类型typing.这些支持索引.

# generic List, supports indexing.
from typing import List

# In this case, the type is easily inferred as type: int.
i = 0

# Even though the type can be inferred as of type list
# there is no way to know the contents of this list.
# By using type: List[str] we indicate we want to use a list of strings.
a = []  # type: List[str]

# Appending an int to our list
# is statically not correct.
a.append(i)

# Appending a string is fine.
a.append("i")

print(a)  # [0, 'i']
Run Code Online (Sandbox Code Playgroud)

如果我们将这些命令添加到文件并使用我们的解释器执行它们,一切正常,print(a)只需打印列表的内容a.这些# type评论已被废弃,被视为没有额外语义含义的简单评论.

mypy另一方面,通过运行此操作,我们得到以下响应:

(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
Run Code Online (Sandbox Code Playgroud)

指示str对象列表不能包含int,静态地说,它是声音.这可以通过遵守a仅附加str对象的类型或通过更改内容的类型来固定,a以指示任何值都是可接受的(使用List[Any]after Any导入后直观地执行typing).

函数注释param_name : type在函数签名中的每个参数之后添加在表单中,并且使用-> type结束函数冒号之前的符号指定返回类型; 所有注释都__annotations__以便利的字典形式存储在该函数的属性中.使用一个简单的例子(不需要typing模块中的额外类型):

def annotated(x: int, y: str) -> bool:
    return x < y
Run Code Online (Sandbox Code Playgroud)

annotated.__annotations__属性现在具有以下值:

{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
Run Code Online (Sandbox Code Playgroud)

如果我们是一个完整的noobie,或者我们熟悉Py2.7概念并因此不知道TypeError潜伏在比较中annotated,我们可以执行另一个静态检查,捕获错误并为我们节省一些麻烦:

(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
Run Code Online (Sandbox Code Playgroud)

除此之外,调用具有无效参数的函数也会被捕获:

annotated(20, 20)

# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
Run Code Online (Sandbox Code Playgroud)

这些可以扩展到基本上任何用例,并且捕获的错误比基本调用和操作进一步扩展.你可以检查的类型非常灵活,我只是给它潜在的一个小的潜行峰值.通过查看typing模块,PEP或mypy文档,您可以更全面地了解所提供的功能.

存根文件:

存根文件可用于两种不同的非互斥情况:

  • 您需要键入检查您不希望直接更改功能签名的模块
  • 您希望编写模块并进行类型检查,但还希望将注释与内容分开.

哪些存根文件(扩展名为.pyi)是您正在/想要使用的模块的带注释的接口.它们包含要对丢弃的函数体进行类型检查的函数的签名.为了感觉到这一点,给定一个名为的模块中的三个随机函数randfunc.py:

def message(s):
    print(s)

def alterContents(myIterable):
    return [i for i in myIterable if i % 2 == 0]

def combine(messageFunc, itFunc):
    messageFunc("Printing the Iterable")
    a = alterContents(range(1, 20))
    return set(a)
Run Code Online (Sandbox Code Playgroud)

我们可以创建一个存根文件randfunc.pyi,如果我们愿意,我们可以在其中设置一些限制.缺点是有人在没有存根的情况下查看源代码时,在尝试理解应该传递到哪里时,将不会真正获得注释帮助.

无论如何,存根文件的结构非常简单:使用空体(pass填充)添加所有函数定义,并根据您的要求提供注释.在这里,我们假设我们只想使用int容器的类型.

# Stub for randfucn.py
from typing import Iterable, List, Set, Callable

def message(s: str) -> None: pass

def alterContents(myIterable: Iterable[int])-> List[int]: pass

def combine(
    messageFunc: Callable[[str], Any],
    itFunc: Callable[[Iterable[int]], List[int]]
)-> Set[int]: pass
Run Code Online (Sandbox Code Playgroud)

combine函数指示了为什么您可能希望在不同的文件中使用注释,它们有时会使代码混乱并降低可读性(Python的大禁忌).你当然可以使用类型别名,但有时混淆比它更有帮助(所以明智地使用它们).


这应该让您熟悉Python中类型提示的基本概念.即使使用了类型检查器, mypy您应该逐渐开始看到更多弹出窗口,一些内部在IDE(PyCharm)中,另一些在内部作为标准python模块.如果我找到它们(或者如果建议的话),我会尝试在以下列表中添加其他检查器/相关包.

我知道的跳棋:

  • Mypy:如此处所述.
  • PyType:谷歌使用与我收集的不同的符号,可能值得一看.

相关软件包/项目:

  • typeshed:官方Python repo,包含标准库的各种存根文件.

typeshed项目实际上是您可以查看如何在您自己的项目中使用类型提示的最佳位置之一.我们以相应文件中__init__dundersCounter为例.pyi:

class Counter(Dict[_T, int], Generic[_T]):
        @overload
        def __init__(self) -> None: ...
        @overload
        def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
        @overload
        def __init__(self, iterable: Iterable[_T]) -> None: ...
Run Code Online (Sandbox Code Playgroud)

在哪里_T = TypeVar('_T')用于定义泛型类.对于Counter类,我们可以看到它可以在其初始化程序中不接受任何参数,Mapping从任何类型获取单个int 接受Iterable任何类型.


注意:我忘记提到的一件事是该typing模块是临时引入的.从PEP 411:

临时包可以在"逐渐"进入"稳定"状态之前修改其API.一方面,这种状态为包提供了正式成为Python发行版的一部分.另一方面,核心开发团队明确指出,不会对包的API的稳定性做出任何承诺,这可能会在下一个版本中发生变化.虽然这被视为不太可能的结果,但如果对其API或维护的担忧证明是有充分理由的话,甚至可以在没有弃用期的情况下从标准库中删除此类包.

所以带着一点盐在这里拿东西; 我怀疑它会在很大程度上被移除或改变,但人们永远不会知道.


**另一个主题完全但在类型提示的范围内有效PEP 526::变量注释的语法# type通过引入允许用户在简单varname: type语句中注释变量类型的新语法来替换注释的努力.

请参阅Python 3.6中的什么是变量注释?如前所述,对于这些介绍的小介绍.

  • “由于Python具有高度动态性,因此推断或检查所使用对象的类型特别困难。” 您是指静态检查,对吗? (2认同)

Ani*_*non 41

加入Jim的精心解答:

检查typing模块 - 该模块支持PEP 484指定的类型提示.

例如,下面的函数获取并返回type的值,str并注释如下:

def greeting(name: str) -> str:
    return 'Hello ' + name
Run Code Online (Sandbox Code Playgroud)

typing模块还支持:

  1. 键入别名.
  2. 键入提示回调函数.
  3. 泛型 - 已扩展抽象基类以支持订阅以表示容器元素的预期类型.
  4. 用户定义的泛型类型 - 用户定义的类可以定义为泛型类.
  5. 任何类型 - 每种类型都是Any的子类型.


tsv*_*son 25

新发布的PyCharm 5支持类型提示.在他们关于它的博客文章中(参见PyCharm 5中的Python 3.5类型提示),它们提供了关于什么类型提示的很好解释,并没有提供有关如何在代码中使用它们的几个示例和插图.

此外,它在Python 2.7中受支持,如本评论中所述:

PyCharm支持PyPI for Python 2.7,Python 3.2-3.4的输入模块.对于2.7,您必须将类型提示放在*.pyi存根文件中,因为函数注释是在Python 3.0中添加的.