高阶Python|返回类型提示技巧 (3)

简介: 高阶Python|返回类型提示技巧 (3)

引言

Python提供了一种可选的特性——类型提示,它有助于提高代码的可读性、可推理性和可调试性。通过类型提示,开发者能够清楚地了解变量、函数参数和返回值应具备的数据类型。在开发那些需要高度灵活性的应用程序时,您可能会需要为代码指定多种可能的返回类型,这样做可以使您的程序更加健壮,更能适应多变的运行环境。

在实际开发中,您可能会碰到需要在Python函数中标注多种返回类型的情况。这意味着函数返回的数据类型不是单一的,而是多样的。本文将通过实例向您展示,如何为一个从电子邮件地址中解析出域名的函数定义多种可能的返回类型。同时,您还将学习到如何为那些接受函数作为参数或者作为回调的函数添加类型提示。通过这些示例,您将能够更自如地在函数式编程中使用类型提示。

利用类型别名提升代码可读性

您已经了解到如何利用类型注解来定义多种类型,现在让我们来看看如何通过最佳实践来提高代码的可维护性。首先是关于类型别名的概念。

如果您在多个函数中反复使用相同的返回类型,那么在代码库的各个地方分别维护它们可能会非常麻烦。在这种情况下,您可以考虑定义一个类型别名。通过为一组类型注安上别名,您可以在代码中的多个函数里复用这个别名。

这样做的好处在于,如果您需要对这组类型注进行修改,只需在一个位置进行更改,而无需逐个函数去调整它们的返回类型。

您可以通过为类型注解定义一个别名,然后用这个别名作为类型注解来实现这一点。以下是对之前函数使用类型别名的一个示例:

EmailComponents = tuple[str, str] | None

def parse_email(email_address: str) -> EmailComponents:
    if "@" in email_address:
        username, domain = email_address.split("@")
        return username, domain
    return None

在这段内容中,您创建了一个名为 EmailComponents 的新变量,用来作为函数返回值类型的别名。这个返回值可以是 None,也可以是一个包含两个字符串的元组。接着,您在函数定义中使用了这个 EmailComponents 别名。

从 Python 3.10 开始,引入了 TypeAlias 语法,让类型别名的声明更加清晰,并且易于与普通变量区分。以下是如何应用这一新特性的示例:

from typing import TypeAlias

EmailComponents: TypeAlias = tuple[str, str] | None

在使用 TypeAlias 对 EmailComponents 别名进行类型注解之前,您需要先从 typing 模块中导入它。导入完成后,您便可以按照之前的例子,将其用作类型别名的类型提示。

请注意,自 Python 3.12 版本起,您可以采用新的软关键字 type 来定义类型别名。软关键字 type 在上下文中明确表示其为关键字时才具有特殊含义,否则它可能被解释为其他意义。别忘了,在 Python 中,type() 同时还是一个内建函数。以下是使用这个新软关键字 type 的示例方法:

type EmailComponents = tuple[str, str] | None

自 Python 3.12 版本起,您可以通过 type 关键字来定义类型别名,正如前述示例所示。这种方式允许您直接指定别名名称及其对应的类型注解。一个显著的优势是,使用 type 定义类型别名时,您无需导入任何额外的模块。

将类型注解赋予具有描述性和实际意义的别名,这种方法虽然简单,却能有效提升代码的可读性和可维护性,因此值得您在编程实践中考虑并应用。

使用静态类型检查工具提升代码质量

Python 作为一门动态类型语言,在运行时不会实际执行类型注解。这表示函数可以声明它想要的任何返回类型,而程序在实际没有返回指定类型值或抛出异常的情况下也能正常运行。

虽然 Python 运行时不强制类型注解,但您可以利用第三方类型检查工具来辅助,这些工具有些还能通过插件与您的代码编辑器结合使用。它们在开发或测试阶段帮助您发现与类型相关的错误。

Mypy 是一个广泛使用的 Python 静态类型检查工具。除此之外,还有 pytype、Pyre 和 Pyright 等其他选择。这些工具通过分析变量的赋值来推断其类型,并与类型注解进行比对。

要在您的项目中应用 mypy,首先需要在您的虚拟环境中通过 pip 命令安装 mypy 包:

(venv) $ python -m pip install mypy

通过这个操作,mypy 工具将被集成到您的项目里。设想一下,如果您在包含之前讨论过的某个函数的 Python 模块上执行 mypy 命令,会看到什么结果?

回想一下先前提到的 parse_email() 函数,它使用一个包含电子邮件地址的字符串作为输入参数。该函数的返回值可能是 None,或者是一个包含用户名与域名信息的字符串元组。如果您还没有将这个函数保存起来,请将其保存在名为 email_parser.py 的文件中。

# email_parser.py

def parse_email(email_address: str) -> tuple[str, str] | None:
    if "@" in email_address:
        username, domain = email_address.split("@")
        return username, domain
    return None

您可以通过类型检查器运行此代码,方法是在命令行中键入 mypy,后跟 Python 文件的名称:

(venv) $ mypy email_parser.py
Success: no issues found in 1 source file

这种方式会在不运行您的代码的前提下,自动进行静态代码分析。Mypy 会尝试判断实际的值是否符合类型注解所声明的预期类型。就目前的情况而言,一切似乎都正确无误。

然而,如果您在代码中出了个差错,会怎样呢?例如,如果 parse_email() 函数的返回值类型注解有误,错误地声明为一个字符串,而不是一个包含两个字符串的元组:

# email_parser.py

def parse_email(email_address: str) -> str | None:
    if "@" in email_address:
        username, domain = email_address.split("@")
        return username, domain
    return None

上面修改后的 parse_email() 函数在类型提示和它实际返回的值之一之间存在差异。当您在命令行中重新运行 mypy 时,您将看到以下错误:

(venv) $ mypy email_parser.py
email_parser.py:6: error: Incompatible return value type
⮑ (got "tuple[str, str]", expected "str | None")  [return-value]
Found 1 error in 1 file (checked 1 source file)

这个提示说明您的函数返回了一个包含两个字符串的元组,而不是预期的单个字符串。这类信息极为重要,因为它有助于避免在程序运行过程中出现严重的错误。

mypy 不仅可以进行类型检查,还能够自动推断类型。当您使用 mypy 运行脚本时,可以将任何表达式或变量传递给 reveal_type() 函数,无需事先导入该函数。reveal_type() 会推断出表达式的类型,并将结果输出到标准输出。

在为函数添加类型注解的过程中,reveal_type() 函数在处理一些较为复杂的情况时非常有用。以下是一个示例,展示了如何利用 reveal_type() 函数来确定 parse_email() 函数返回值的实际类型:

# email_parser.py

# ...

result = parse_email("claudia@realpython.com")
reveal_type(result)

在上面的示例中,变量 result 包含来自解析的电子邮件地址的两个组成部分。您可以将此变量传递到reveal_type()函数中,以便mypy推断类型。以下是在控制台中运行它的方法:

(venv) $ mypy email_parser.py
email_parser.py:10: note: Revealed type is
⮑ "Union[tuple[builtins.str, builtins.str], None]"
Success: no issues found in 1 source file

当您对 Python 文件执行 mypy 检查时,它会输出 reveal_type() 函数所推断的类型信息。例如,mypy 能够准确识别出结果变量是一个包含两个字符串的元组,或者是 None 这种空值。

请务必使用 reveal_type() 这样的实用功能,它可以帮您清晰地了解复杂返回类型的类型信息。

总结

类型注解虽非强制要求,但它能提升代码的可读性、易用性和调试效率。类型注解向其他开发者清晰表明了函数的输入和输出类型,有助于团队协作。

在本教程中,我们专注于在更复杂的场景中应用类型注解,并学习了如何有效使用和维护它们。

通过本教程,您已经学会了:

  • 使用管道符 (|) 或 Union 类型来标注一个函数可能返回的不同类型
  • 使用元组来指定函数返回的多个数据项各自的类型
  • 使用 Callable 类型来注解回调函数
  • 使用 Generator、Iterator 和 Iterable 类型来注解生成器函数
  • 使用类型别名简化代码中多处使用的复杂类型注解
  • 应用 Mypy 这个第三方类型检查工具

现在,您可以在多种不同的编程情境中运用类型注解。您通常是如何在代码中使用类型注解的?欢迎在评论区分享您的实际应用案例。

相关文章
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
650 2
|
IDE 开发工具 开发者
Python类型注解:提升代码可读性与健壮性
Python类型注解:提升代码可读性与健壮性
559 102
|
索引 Python
Python的变量和简单类型
本文介绍了Python中变量命名规则、常用变量类型及字符串操作。变量命名需遵循字母、数字和下划线组合,不能以数字开头且不可与关键字冲突。字符串支持单引号、双引号或三引号定义,涵盖基本输出、转义字符、索引、拼接等操作。此外,还详细解析了字符串方法如`islower()`、`upper()`、`count()`等,帮助理解字符串处理技巧。
422 15
|
安全 JavaScript Java
Python中None与NoneType的真相:从单例对象到类型系统的深度解析
本文通过10个真实场景,深入解析Python中表示“空值”的None与NoneType。从单例模式、函数返回值,到类型注解、性能优化,全面揭示None在语言设计与实际编程中的核心作用,帮助开发者正确高效地处理“无值”状态,写出更健壮、清晰的Python代码。
4555 3
|
Python
Python技术解析:了解数字类型及数据类型转换的方法。
在Python的世界里,数字并不只是简单的数学符号,他们更多的是一种生动有趣的语言,用来表达我们的思维和创意。希望你从这个小小的讲解中学到了有趣的内容,用Python的魔法揭示数字的奥秘。
362 26
|
缓存 数据可视化 Linux
Python文件/目录比较实战:排除特定类型的实用技巧
本文通过四个实战案例,详解如何使用Python比较目录差异并灵活排除特定文件,涵盖基础比较、大文件处理、跨平台适配与可视化报告生成,助力开发者高效完成目录同步与数据校验任务。
427 0
|
IDE API 开发工具
Python类型注解:让代码“开口说话”的隐形助手
Python类型注解为动态语言增添类型信息,提升代码可读性与健壮性。通过变量、函数参数及返回值的类型标注,配合工具如mypy、IDE智能提示,可提前发现类型错误,降低调试与协作成本。本文详解类型注解的实战技巧、生态支持及最佳实践,助你写出更高质量的Python代码。
591 0
|
人工智能 安全 IDE
Python 的类型安全是如何实现的?
本文探讨了 Python 的类型安全实现方式。从 3.5 版本起,Python 引入类型提示(Type Hints),结合静态检查工具(如 mypy)和运行时验证库(如 pydantic),增强类型安全性。类型提示仅用于开发阶段的静态分析,不影响运行时行为,支持渐进式类型化,保留动态语言灵活性。泛型机制进一步提升通用代码的类型安全性。总结而言,Python 的类型系统是动态且可选的,兼顾灵活性与安全性,符合“显式优于隐式”的设计哲学。
450 2
|
Python Windows
[oeasy]python076_int这个词怎么来的_[词根溯源]整数类型_int_integer_touch
本文探讨了“int”一词的起源及其与整数类型的关联。通过词根溯源,揭示“int”来源于“integer”,意为“完整的数”,与零碎的分数相对。同时分析了相关词汇如“tact”(接触)、“touch”(触摸)及衍生词,如“tangential”(切线的)、“intagible”(无形的)和“integral”(完整的、不可或缺的)。文章还结合编程语言特性,解释了Python作为动态类型、强类型语言的特点,并总结了整型变量的概念与意义。最后预告了后续内容,提供了学习资源链接。
696 11
|
存储 C语言 Python
[oeasy]python077_int类型怎么用_整数运算_integer_进制转化_int类
本文主要讲解了Python中`int`类型的应用与特性。首先回顾了`int`词根的溯源,探讨了整型变量的概念及命名规则(如匈牙利命名法)。接着分析了整型变量在内存中的存储位置和地址,并通过`type()`和`id()`函数验证其类型和地址。还介绍了整型变量的运算功能,以及如何通过`int()`函数将字符串转化为整数,支持不同进制间的转换(如二进制转十进制)。此外,文章提及了关键字`del`的使用场景,对比了Python与C语言中`int`的区别,并总结了整型与字符串类型的差异,为后续深入学习奠定基础。
535 1

推荐镜像

更多