Python 3.5 中的类型提示是什么?
- 2024-11-21 08:33:00
- admin 原创
- 4
问题描述:
Python 3.5 中最受关注的功能之一是类型提示。
本文和本文中提到了类型提示的示例,同时还提到要负责任地使用类型提示。有人可以详细解释一下它们以及何时应该使用它们,何时不应该使用它们吗?
解决方案 1:
我建议阅读PEP 483和PEP 484,并观看Guido关于类型提示的演示。
简而言之:类型提示就是字面意思。提示您正在使用的对象的类型。
由于Python 的动态特性,推断或检查正在使用的对象的类型尤其困难。这一事实使得开发人员很难理解他们没有编写的代码中到底发生了什么,最重要的是,许多 IDE(比如PyCharm和PyDev)中的类型检查工具由于没有任何对象类型的指示而受到限制。因此,他们只能尝试推断类型(如演示文稿中所述),成功率约为 50%。
从类型提示演示文稿中取出两张重要的幻灯片:
为什么要使用类型提示?
帮助类型检查器:通过提示您希望对象是什么类型,类型检查器可以轻松检测到,例如,您是否传递了一个具有非预期类型的对象。
有助于文档编制:第三人查看您的代码将知道在哪里需要什么,因此,无需获取它们即可了解如何使用它
TypeErrors
。帮助 IDE 开发更准确、更强大的工具:开发环境在知道对象类型时更适合建议适当的方法。您可能在某些时候使用某些 IDE 时遇到过这种情况,点击后
.
会弹出未为对象定义的方法/属性。
为什么要使用静态类型检查器?
尽早发现错误:我相信,这是不言而喻的。
项目越大,就越需要它:同样,这很有道理。静态语言提供了动态语言所缺乏的稳健性和控制力。应用程序越大越复杂,就越需要控制力和可预测性(从行为方面来看)。
大型团队已经在运行静态分析:我猜这验证了前两点。
作为这个小介绍的结束语:这是一个可选功能,据我所知,引入它是为了获得静态类型的一些好处。
您通常不需要担心它,也绝对不需要使用它(特别是在使用 Python 作为辅助脚本语言的情况下)。它在开发大型项目时应该会很有帮助,因为它提供了急需的稳健性、控制和额外的调试功能。
使用 mypy 进行类型提示:
为了使这个答案更加完整,我认为进行一些演示是合适的。我将使用mypy
,这是启发 PEP 中呈现的类型提示的库。这主要是为遇到这个问题并想知道从哪里开始的人写的。
在此之前,让我重申以下几点:PEP 484不会强制执行任何操作;它只是为函数注释设定了一个方向,并提出了可以/应该如何执行类型检查的指导方针。您可以注释您的函数并根据需要提示任意多的内容;无论是否存在注释,您的脚本仍将运行,因为 Python 本身不使用它们。
无论如何,正如 PEP 中所述,提示类型通常应采用三种形式:
函数注释(PEP 3107)。
内置/用户模块的存根文件。
特殊
# type: type
注释是对前两种形式的补充。(请参阅:什么是变量注释?,了解 Python 3.6 注释更新# type: type
)
此外,您还需要将类型提示与typing
中引入的新模块结合使用Py3.5
。其中,定义了许多(额外的) ABCcollections.abc
(抽象基类)以及用于静态检查的辅助函数和装饰器。 中的大多数 ABC 都包括在内,但采用通用形式以允许订阅(通过定义__getitem__()
方法)。
对于那些想更深入地解释这些内容的人来说,这篇mypy documentation
文章写得非常好,并且有很多代码示例来演示/描述他们的检查器的功能;绝对值得一读。
函数注释及特别说明:
首先,观察一下使用特殊注释时我们能得到的一些行为是很有趣的。# type: type
如果无法直接推断出对象的类型,可以在变量赋值期间添加特殊注释来指示对象的类型。简单赋值通常很容易推断,但其他赋值(如列表(就其内容而言))则不能。
注意:如果我们想使用任何容器的衍生品,并且需要指定该容器的内容,我们必须使用模块中的通用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']
如果我们将这些命令添加到文件中并用我们的解释器执行它们,一切都会正常工作,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"
表示对象列表str
不能包含int
,从静态角度来说,这是合理的。可以通过遵守 的类型a
并仅附加str
对象或通过更改 的内容的类型来修复此问题,以指示任何值都是可以接受的(在从 导入后a
直观地执行)。List[Any]
`Any`typing
函数注释以表单形式添加到函数签名中的每个参数之后,并使用结束函数冒号之前的符号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 的概念,因此不知道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")
除此之外,使用无效参数调用函数也会被捕获:
annotated(20, 20)
# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
这些可以扩展到基本上任何用例,并且捕获的错误不仅限于基本调用和操作。您可以检查的类型非常灵活,我只是对其潜力进行了小小的介绍。查看模块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)
我们可以创建一个存根文件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
该combine
函数表明了为什么您可能希望在不同文件中使用注释,它们有时会使代码变得混乱并降低可读性(对于 Python 来说是大忌)。当然,您可以使用类型别名,但有时这样做会造成混淆,而不是带来帮助(因此请明智地使用它们)。
这应该能让你熟悉 Python 中类型提示的基本概念。尽管使用的类型检查器已经存在,但mypy
你应该会逐渐开始看到更多类型提示出现,一些在 IDE(PyCharm)内部出现,另一些则作为标准 Python 模块出现。
当我找到其他检查器/相关包时(或有建议时),我会尝试将其添加到以下列表中。
我知道的跳棋有:
Mypy:如此处所述。
PyType:由 Google 提供,使用与我所收集到的不同符号,可能值得一看。
相关软件包/项目:
typeshed:官方 Python 存储库,包含标准库的各种存根文件。
该typeshed
项目实际上是您可以查看如何在您自己的项目中使用类型提示的最佳位置之一。让我们以相应文件中的类的 dunders为例__init__
`Counter`.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: ...
其中_T = TypeVar('_T')
用于定义泛型类。对于Counter
该类,我们可以看到它可以在其初始化程序中不采用任何参数,Mapping
从任意类型获取单个参数int
,或者采用Iterable
任意类型的参数。
注意:我忘记提的一件事是,该typing
模块是临时引入的。来自PEP 411:
临时软件包的 API 可能会在“升级”为“稳定”状态之前进行修改。一方面,这种状态为软件包提供了成为 Python 发行版正式组成部分的好处。另一方面,核心开发团队明确表示,不保证软件包 API 的稳定性,这可能会在下一个版本中发生变化。虽然这被认为是不太可能出现的结果,但如果对其 API 或维护的担忧得到充分证实,此类软件包甚至可能会从标准库中删除,而无需经过弃用期。
因此,请对此持保留态度;我怀疑它会被删除或以重大方式改变,但谁也不知道。
**完全是另一个主题,但在类型提示范围内有效::PEP 526
变量注释的语法是通过引入新语法来取代# type
注释的一种努力,允许用户在简单的语句中注释变量的类型varname: type
。
请参阅前面提到的什么是变量注释?以获得对这些内容的简单介绍。
解决方案 2:
补充吉姆的详细回答:
检查typing
模块——该模块支持PEP 484指定的类型提示。
例如,下面的函数接受并返回类型的值str
,注释如下:
def greeting(name: str) -> str:
return 'Hello ' + name
该typing
模块还支持:
类型别名。
回调函数的类型提示。
泛型- 抽象基类已扩展为支持订阅来表示容器元素的预期类型。
用户定义的泛型类型- 用户定义的类可以定义为泛型类。
任何类型- 每种类型都是任何类型的子类型。
解决方案 3:
新发布的 PyCharm 5 支持类型提示。在关于它的博客文章中(请参阅PyCharm 5 中的 Python 3.5 类型提示),他们很好地解释了什么是类型提示,什么不是类型提示,以及如何在代码中使用它们的几个示例和插图。
此外,它在 Python 2.7 中也受支持,如下评论所述:
PyCharm 支持 PyPI 中针对 Python 2.7、Python 3.2-3.4 的 typing 模块。对于 2.7,您必须将类型提示放在 .pyi 存根文件中,因为函数注释是在 Python 3.0 中添加的*。
解决方案 4:
类型提示是为了可维护性,不会被 Python 解释。在下面的代码中,这一行def add(self, ic:int)
直到下一行才导致错误return...
:
class C1:
def __init__(self):
self.idn = 1
def add(self, ic: int):
return self.idn + ic
c1 = C1()
c1.add(2)
c1.add(c1)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 5, in add
TypeError: unsupported operand type(s) for +: 'int' and 'C1'
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件