Python 3.5 中的类型提示是什么?

2024-11-21 08:33:00
admin
原创
4
摘要:问题描述:Python 3.5 中最受关注的功能之一是类型提示。本文和本文中提到了类型提示的示例,同时还提到要负责任地使用类型提示。有人可以详细解释一下它们以及何时应该使用它们,何时不应该使用它们吗?解决方案 1:我建议阅读PEP 483和PEP 484,并观看Guido关于类型提示的演示。简而言之:类型提示...

问题描述:

Python 3.5 中最受关注的功能之一是类型提示

本文和本文中提到了类型提示的示例,同时还提到要负责任地使用类型提示。有人可以详细解释一下它们以及何时应该使用它们,何时不应该使用它们吗?


解决方案 1:

我建议阅读PEP 483和PEP 484,并观看Guido关于类型提示的演示。

简而言之类型提示就是字面意思。提示您正在使用的对象的类型

由于Python 的动态特性,推断或检查正在使用的对象的类型尤其困难。这一事实使得开发人员很难理解他们没有编写的代码中到底发生了什么,最重要的是,许多 IDE(比如PyCharm和PyDev)中的类型检查工具由于没有任何对象类型的指示而受到限制。因此,他们只能尝试推断类型(如演示文稿中所述),成功率约为 50%。


从类型提示演示文稿中取出两张重要的幻灯片:

为什么要使用类型提示?

  1. 帮助类型检查器:通过提示您希望对象是什么类型,类型检查器可以轻松检测到,例如,您是否传递了一个具有非预期类型的​​对象。

  2. 有助于文档编制:第三人查看您的代码将知道在哪里需要什么,因此,无需获取它们即可了解如何使用它TypeErrors

  3. 帮助 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模块还支持:

  1. 类型别名。

  2. 回调函数的类型提示。

  3. 泛型- 抽象基类已扩展为支持订阅来表示容器元素的预期类型。

  4. 用户定义的泛型类型- 用户定义的类可以定义为泛型类。

  5. 任何类型- 每种类型都是任何类型的子类型。

解决方案 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'
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用