Python 3.5中最受关注的特性之一是type hints.
this article和this one中提到了type hints的例子,同时也提到了负责任地使用类型提示.有人能详细解释一下它们,什么时候应该使用,什么时候不应该使用吗?
Python 3.5中最受关注的特性之一是type hints.
this article和this one中提到了type hints的例子,同时也提到了负责任地使用类型提示.有人能详细解释一下它们,什么时候应该使用,什么时候不应该使用吗?
我建议你读PEP 483和PEP 484,在打字提示上看this presentation和Guido.
In a nutshell: Type hinting is literally what the words mean. You hint the type of the object(s) you're using.
由于Python的dynamic特性,正在使用的对象的inferring or checking the type特别困难.这一事实使得开发人员很难理解他们没有编写的代码中到底发生了什么,最重要的是,对于在许多IDE(想到PyCharm和PyDev)中找到的类型判断工具来说,由于没有任何关于对象类型的指示符,这些工具是有限的.因此,他们求助于try 推断类型(如演示中提到的),成功率约为50%.
要从文字提示演示文稿中拍摄两张重要幻灯片,请执行以下操作:
.
,并且弹出了没有为对象定义的方法/属性.As a closing note for this small introduction:这是一个optional特性,据我所知,它的引入是为了获得静态输入的一些好处.
通常情况下,do not需要担心它,definitely不需要使用它(尤其是在使用Python作为辅助脚本语言的情况下).当开发大型项目时,它应该是有用的.
为了使这个答案更完整,我认为稍微演示一下比较合适.我将使用mypy
,当类型提示出现在PEP中时,它激发了类型提示的灵感.这篇文章主要是为遇到这个问题并想知道从哪里开始的任何人写的.
在此之前,让我重申以下几点:PEP 484不强制任何内容;它只是设置函数的方向 可以/应该执行how类型判断的注释和建议指南.您可以注释您的函数和 可以提示任意多的内容;不管是否存在注释,您的脚本仍将运行,因为Python本身并不使用它们.
无论如何,正如政治公众人物中所指出的,暗示类型通常应采取三种形式:
# type: type
条特别注释.(参见:100获取Python 3.6更新,获得# type: type
条 comments )此外,您还需要将类型提示与Py3.5
中引入的新typing
模块结合使用.在它中,许多(附加的)ABCs(抽象基类)与用于静态判断的辅助函数和decorator 一起定义.collections.abc
中的大多数ABC都包括在内,但采用generic形式,以便允许订阅(通过定义__getitem__()
方法).
对于任何有兴趣更深入地解释这些的人来说,mypy documentation
写得非常好,有很多代码示例演示/描述他们的判断器的功能;这绝对值得一读.
首先,观察我们在使用特殊注释时可以得到的一些行为是很有趣的.特别# type: type
条 comments
可以在变量赋值期间添加,以指示对象的类型(如果无法直接推断).简单的任务是
通常很容易推断,但其他如列表(就其内容而言)则不容易推断.
Note:如果我们想要使用containers的任何派生,并且需要指定该容器的内容,我们must使用来自typing
模块的103种类型.These support indexing.
# 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']
如果我们将这些命令添加到一个文件中,并用解释器执行它们,一切都会正常工作,print(a)
个命令就会打印出来
另一方面,通过使用mypy
运行此命令,我们将获得以下响应:
(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
表示一个包含str
个对象的列表不能包含int
,从静态Angular 讲,int
是声音.这可以通过遵循a
的类型并仅附加str
个对象来解决,或者通过更改a
内容的类型来表明任何值都是可接受的(从typing
导入Any
后直观地使用List[Any]
执行).
函数注释以param_name : type
的形式添加在函数签名中的每个参数之后,并在结束函数冒号之前使用-> type
符号指定返回类型;所有注释都以方便的字典形式存储在该函数的__annotations__
属性中.使用一个简单的示例(不需要typing
模块中的额外类型):
def annotated(x: int, y: str) -> bool:
return x < y
现在,annotated.__annotations__
属性具有以下值:
{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
如果我们是一个完全的新手,或者我们熟悉Python 2.7的概念,因此不知道annotated
的比较中隐藏的TypeError
,那么我们可以执行另一个静态判断,捕捉错误并为我们节省一些麻烦:
(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
此外,使用无效参数调用函数也会被捕获:
annotated(20, 20)
# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
这些可以扩展到基本上任何用例,捕获的错误可以扩展到基本调用和操作之外.你喜欢的类型
存根文件可用于两种不同的非互斥情况:
存根文件(扩展名为.pyi
)是您正在制作/想要使用的模块的带注释的接口.它们包含
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)
我们可以创建存根文件randfunc.pyi
,如果我们希望的话,可以在其中设置一些限制.不利的一面是
没有存根的查看源代码的人在try 理解应该是什么时,不会真正得到注释帮助
从哪里经过.
总之,存根文件的 struct 非常简单:添加所有具有空实体(pass
个填充)的函数定义,然后
# 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
combine
函数给出了为什么要在不同的文件中使用注释的指示,有时注释会变得杂乱无章
这将使您熟悉Python中类型提示的基本概念.即使使用了类型判断器
当我找到它们时(或者如果建议的话),我会try 在下面的列表中添加额外的checker/相关包.
100:
100:
typeshed
项目实际上是您可以查看如何在您自己的项目中使用类型提示的最佳位置之一.让我们以对应的.pyi
文件中的the __init__
dunders of the Counter
class为例:
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: ...
Where _T = TypeVar('_T')
is used to define generic classes.对于Counter
类,我们可以看到它要么在初始值设定项中不带参数,要么从任何类型得到一个Mapping
,要么从任何类型得到一个int
,要么从任何类型得到一个Iterable
.
Notice:有一件事我忘了提到,typing
模块是在provisional basis上引入的.从100开始:
临时包可能在"毕业"到"稳定"状态之前修改其API.一方面,这种状态为包提供了正式成为Python发行版一部分的好处.另一方面,核心开发团队明确表示,没有就包的API的 solidity 做出任何promise ,这可能会在下一版本中发生变化.虽然这被认为是一种不太可能的结果,但如果有关API或维护的担忧被证明是有根据的,甚至可以在没有弃用期的情况下从标准库中删除这些包.
所以,在这里用一小撮盐来装东西;我怀疑它是否会被删除或以重大方式改变,但人们永远无法知道.
**另一个主题,但在类型提示的范围内有效:PEP 526
: Syntax for Variable Annotations是一种通过引入新语法来取代# type
条注释的努力,该语法允许用户在简单的varname: type
语句中注释变量的类型.
如前所述,请参阅100,以了解这些功能的小介绍.