欢迎,来自IP地址为:216.73.216.13 的朋友
Python 语言在函数定义时会使用箭头符(->)来表明函数返回值的预期类型。这个符号虽然是可选使用的,但是如果在函数定义时使用了它,那么就可以明确知晓返回值的数据类型。
例如如下函数的定义:
def get_number_of_titles(titles: list[str]) -> int:
return len(titles)
由于很多 Python 代码并不使用以上的语法,这也会上很多人产生疑惑,”->”符号是什么作用,本文将对箭头符号的作用和用法进行讲解。
简言之:-> 在 Python 用于指定函数返回值的类型
在 Python 语言中,每个数值都会以一定的类型方式保存在变量中,但是由于 Python 是动态类型语言,变量的类型并不是固定的,这意味着同一变量可以在任何时候保存不同类型变量。例如一个变量在某一时刻存储了整型数据,之后很有可能会保存字符串型数据。相反的,一些强类型语言例如 C++ 和 Java 等,需要预先定义变量的类型,变量在其生命周期内会与定义的数据类型进行绑定(不能更改)。
对于 Python 语言,以下代码是合法的,并且充分显示了期动态语言的特性:
my_number = 32 my_number = "32"
我们首先声明了一个变量”my_number”,并且对该变量赋值整型数据 32,之后,又为该变量赋值字符串数据”32″,对于 Python 语言来说,对于同一变量赋予不同类型的数值是不会产生任何错误的。
动态类型也意味着可能无法始终知道 Python 函数会返回什么数据类型(因为理论上它确实可以返回任何值)。不过,了解返回类型通常很有用。为了解决这个问题,Python 3.5 引入了可选的类型提示功能,允许开发者指定返回类型。要添加类型提示,可以在函数参数列表后放置一个”->”,并在冒号前写上预期的返回类型。
我们还可以为函数参数添加类型提示,为此,需要在参数名称后放置一个冒号,后跟预期类型,例如 int 或 str。
基本类型提示示例
为了进一步理解类型提示,假设我们正在创建一个 Python 应用程序来管理电子游戏商店的库存。该程序存储游戏列表,跟踪库存数量,并向顾客推荐新游戏。
我们已经在之前介绍的代码示例中看到了类型提示语法,该示例返回游戏商店所售游戏列表中的游戏数量:
def get_number_of_titles(titles: list[str]) -> int:
return len(titles)
games = ["Dragon Quest", "Final Fantasy", "Age of Empires"]
print("Number of titles:", get_number_of_titles(games))
这里可以看到一个类型提示。首先定义了一个名为 get_number_of_titles() 的函数,它接受一个游戏名称列表作为输入。之后,我们为 titles 参数添加一个类型提示,表明该函数接受一个字符串列表。最后,我们还为返回类型添加了另一个类型提示,指定该函数返回一个 int 值。
该函数返回列表的长度,它是一个整型数值。在下一行测试一下,创建一个变量来存储三个游戏名称的列表。当你对列表调用该函数时,验证输出是否为 3。
需要注意的是,在实际应用中,创建一个单独的函数只是为了返回列表的长度可能是多余的。但是,出于演示目的,示例中展示的函数是一种直接演示类型提示概念的方法。
我们可以将类型提示用于任何 Python 类型。示例已经看到了一个返回类型为 int 的示例,还可以演示另一个返回类型为 str 的示例。假设想推荐一款随机游戏供用户尝试。于是可以使用以下简短的示例函数来实现:
def get_game_recommendation(titles: list[str]) -> str:
return random.choice(titles)
print("Random recommendation:", get_game_recommendation(games))
我们定义了一个名为 get_game_recommendation() 的函数,它同样接受一个列表作为参数。这次,返回类型提示表明该函数返回一个字符串。然后,使用之前创建的游戏列表调用该函数,并向用户发送一条消息,推荐一个随机游戏。
我们还可以注释不返回任何内容的函数,考虑以下示例,它搜索并打印出包含给定关键字的游戏名称:
def find_keyword_in_titles(titles: list[str], keyword: str) -> None:
print("Titles that contain the keyword:", keyword)
for game_title in titles:
if keyword in game_title:
print(game_title)
find_keyword_in_titles(games, "es")
我们创建一个 find_keyword_in_titles() 函数,该函数接受一个游戏名称列表和一个关键字作为参数。接下来,添加一个类型提示,指示返回类型为 None,这意味着它不返回任何内容。相反,它会循环遍历列表中的所有游戏名,并打印出所有包含该关键字的游戏。
完成该函数后,就可以使用游戏列表进行测试,并传入”es”作为关键字。该函数将打印出:
Titles that contain the keyword: es Dragon Quest Age of Empires
Python 类型中的关键术语
随着进一步探索本主题,就会遇到几个不同的术语,例如类型注解、类型提示和类型检查,这些术语有时会互换使用。以下是这些术语的定义,以帮助在学习本教程时牢记它们:
- 类型注解:注解是提供有关 Python 值的附加元数据的一种可选方式。具体来说,类型注解描述了程序中使用的值的类型
- 类型提示:根据 Python 文档,类型提示是”用于指定变量、类属性或函数参数或返回值的预期类型的注解。” 类型提示也是可选的
- 类型检查:验证变量和表达式是否与其指定类型匹配并遵循语言规则的过程
当函数返回与提示不同的类型时会发生什么?
正如之前所了解的,类型提示是可选的。Python 解释器实际上不会强制执行它们。因此,如果函数的返回值类型与类型提示不匹配,在标准 Python 中不会遇到任何错误或警告。
考虑以下代码,其中重新访问了返回列表中游戏标题数量的函数:
def get_number_of_titles(titles: list[str]) -> str:
return len(titles)
games = ["Dragon Quest", "Final Fantasy", "Age of Empires"]
print("Number of titles:", get_number_of_titles(games))
Number of titles: 3
该函数与第一次看到它时基本相同,但这次类型提示指示该函数应该返回一个字符串值。这是故意弄错的,以表明 Python 本身不会报错,但类型检查器会报错。请注意,该函数仍然返回一个整数值。当创建列表并调用该函数时,就可以看到它顺利地返回并打印了整数,Python 没有任何报错。
但是,为了增加额外的类型验证级别,我们可以将类型提示与某些工具(例如 Linter 和类型检查器)结合使用,以确保返回值与类型提示匹配。Linter 是一种自动化软件工具,可以分析代码的格式和正确性问题,并标记可能存在的问题。类型检查器是一种检查代码以确保所有数据值和变量都属于预期类型的工具。
Mypy 是一款流行的 Python 类型检查工具。我们可以使用它来验证函数参数和返回值是否与声明的类型提示匹配。可以下载并安装该工具,作为开发工作流程的一部分。我们还可以在线试用 mypy playground,并尝试对自己的代码进行类型检查。
Python -> 之后可以使用哪些类型?
类型提示不仅限于基本数据类型或 Python 内置类型。我们还可以将它们用于列表和字典等集合。在下一个示例中,我们将看到如何注释字典的键和值。该函数处理游戏库存,并仅返回库存最高的游戏:
game_stock = {
"Dragon Quest": 5,
"Final Fantasy": 1,
"Age of Empires": 5
}
def get_highest_stock_games(game_stock: dict[str, int]) -> dict[str, int]:
max_quantity = max(game_stock.values())
return {
game: quantity
for game, quantity in game_stock.items()
if quantity == max_quantity
}
print("Games with the highest stock:", get_highest_stock_games(game_stock))
Games with the highest stock: {'Dragon Quest': 5, 'Age of Empires': 5}
首先定义一个名为 game_stock 的字典,将游戏名称与可用数量配对。然后,创建一个函数 get_highest_stock_games(),该函数接受游戏库存字典作为参数,并返回一个包含字符串和整数对的字典。该字典中的每一对都代表一个游戏名称及其数量,其中包含可用数量最多的游戏。
注意,在此示例中,当我们为复杂的数据结构添加类型提示时,不仅要指定结构的数据类型,还要指定其包含的类型。但是,即使省略元素类型也不会出现错误。例如,简单的”-> dict”类型提示方式也可以工作,但使用 dict[str, int] 被认为是最佳实践,因为它提供了更具体的类型信息。
该函数使用 max() 函数高效地查找最大数量,然后使用字典推导式返回所有具有该最大数量的游戏。正如返回类型提示所指定,该字典由字符串和整数对组成。
最后,返回生成的字典并将其打印到屏幕上。运行代码时,会从输出中看到返回的字典与类型提示匹配。
如果我们创建了自己的类,那么也可以将它们用作返回类型。还可以使用类型提示为一个函数指定多种可能的返回类型。例如,如果要创建一个类来将游戏表示为对象,那么就可能有一个函数,如果游戏有库存,则返回游戏对象,否则返回 None。
-> 符号除函数之外的用法
除了函数之外,我们还可以使用箭头符号来表示类中的方法。例如,假设我们创建了一个 Game 类来表示游戏对象:
class Game:
def __init__(self, name: str, genre: str, price: float) -> None:
self.name = name
self.genre = genre
self.price = price
def get_name(self) -> str:
return self.name
def get_genre(self) -> str:
return self.genre
def get_price(self) -> float:
return self.price
我们定义了一个非常基础的 Game 类,它包含一个构造函数和一些类属性:名称、游戏类型和价格。类构造函数的类型提示指示它返回 None,而属性的 getter 方法的类型提示指示它们分别返回字符串(表示名称)、字符串(表示游戏类型)和浮点数(表示价格)。
使用返回类型提示有哪些好处?
类型提示有助于提高代码的自文档性。与其编写注释,不如使用 -> 语法,让读者只需查看函数签名就能更轻松地了解函数的预期输出类型。
与 Linter 和类型检查器等工具一起使用,类型提示可以帮助我们在错误导致代码问题之前捕获它们。通过确保函数返回正确的类型,就可以避免尝试对错误类型的数据执行无效操作的情况。例如,如果希望对某个返回值执行数学计算,那么肯定希望函数返回的是数字而不是字符串。
使用类型检查器和其他自动化工具以及 -> 语法来验证返回值可以节省开发时间,这些工具可以识别出原本需要花费宝贵时间自行跟踪和调试的问题。
使用返回类型提示有什么缺点吗?
与任何文档一样,开发者必须随着代码的更改而更新返回类型提示。可能需要出于多种原因更新它们,包括:
- 项目需求更新:程序可能需要处理与最初规定不同的数据类型。例如,假设需要将 get_highest_stock_games() 函数更改为返回 Game 对象和整数对,而不是字符串和整数
- 代码重构:函数交互方式的变化可能需要更新返回类型及其注释。例如,假设想在 find_keyword_in_titles() 函数中在程序的其他位置使用匹配的标题,而不是仅仅打印出来。需要将返回类型从 None 更改为 str 或其他类型
- 更正或错误修复:有时只是在代码中犯了错误,例如使用了错误的返回类型。在 Game 类中,可以看到 get_price() 方法返回一个浮点值。如果错误地将 price 属性和 get_price() 的返回类型提示设为整数,则需要更正该逻辑错误
标准 Python 解释器不强制使用类型提示,因此如果不了解此限制,可能会产生一种虚假的安全感。如果预计使用 Linter、类型检查器或其他验证工具,而项目由于某种原因未使用这些工具,那么可能仍然需要进行手动类型验证。
在这种情况下,除了提醒正确的输出应该是什么之外, -> 语法在实践中可能没有那么有用。话虽如此,大多数现代代码编辑器都内置了对类型提示的支持,使其更易于有效使用。
我们学习了 Python 中的 -> 符号,并了解了它的含义和用法。通过一个理论上的电子游戏库存应用程序,系统的演示了类型提示如何应用于函数和类方法。
现在,我们已经了解了类型提示和 -> 语法,并掌握了一项宝贵的技能,可以帮助我们提升代码的可读性和自文档性。持续使用它们,不仅能让开发过程更加顺畅,还能保持更高质量的代码。
