【Python入门教程】第62篇 函数进阶之类型提示

本篇我们介绍 Python 类型提示(type hint)功能,以及如何使用 mypy 工具执行静态类型检查。

类型提示

许多编程语句使用静态类型,例如 C/C++。静态类型意味着我们需要在使用之前声明变量、函数参数以及返回值的类型。预定义的类型使得编译器可以在编译和运行程序之前检查代码。

Python 使用动态类型,变量、函数参数以及返回值可以是任何类型。同时,变量的类型在程序运行期间可以改变。

一般来说,动态类型更容易编写代码,但是也可能会引起预料之外的错误,这些错误只有在程序运行时才会被发现。

Python 的类型提示功能提供了可选的静态类型,可以同时获得静态类型和动态类型的优点。以下示例定义了一个简单的函数,可以接受一个字符串参数并返回另一个字符串:

def say_hi(name):
    return f'Hi {name}'


greeting = say_hi('John')
print(greeting)

以下是为函数参数和返回值添加类型提示的语法:

parameter: type
-> type

以下示例在 say_hi() 函数中使用了类型提示:

def say_hi(name: str) -> str:
    return f'Hi {name}'


greeting = say_hi('John')
print(greeting)

输出结果如下:

Hi John

在新的函数定义中,参数 name 的数据类型为 str:

name: str

函数返回值的类型也是 str:

-> str

除了 str 类型之外,我们也可以使用其他的内置类型,例如 int、float、bool 以及 bytes。

注意,Python 解释器会直接忽略类型提示。如果我们将一个数字作为参数传递给 say_hi() 函数,程序可以正常执行,而不会返回警告或者错误:

def say_hi(name: str) -> str:
    return f'Hi {name}'


greeting = say_hi(123)
print(greeting)

输出结果如下:

Hi 123

为了检查类型提示中的语法,我们需要一个静态类型检查工具。

静态类型检查工具:mypy

Python 没有提供官方的静态类型检查工具。目前最流行的第三方工具是 Mypy,我们需要使用 pip 命名进行安装:

pip instal mypy

安装之后,我们可以使用 mypy 检查代码中的类型提示:

mypy app.py

显示的信息如下:

app.py:5: error: Argument 1 to "say_hi" has incompatible type "int"; expected "str"
Found 1 error in 1 file (checked 1 source file)

以上错误表示 say_hi 函数的实际参数是 int 类型,但实际需要一个 str 类型的参数。

如果我们将参数改回字符串之后再次运行 mypy,将会显示以下成功信息:

Success: no issues found in 1 source file

类型提示与类型推断

当我们定义变量时,可以增加一个类型提示,例如:

name: str = 'John'

变量 name 的类型为 str。如果我们一个非字符串的值赋予变量 name,静态类型检查器将会返回错误。例如:

name: str = 'Hello'
name = 100
app.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str")
Found 1 error in 1 file (checked 1 source file)

通常不需要为变量指定一个类型,因为静态类型检查器可以基于变量的值推断它的类型。以下示例中,name 的值时一个字符串常量,因此静态类型检查器推断它的类型为 str:

name = 'Hello'
name = 100

程序同样会返回类型错误:

app.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str")
Found 1 error in 1 file (checked 1 source file)

支持多种类型的类型提示

以下 add() 函数返回了两个数字的求和:

def add(x, y):
    return x + y

函数的参数可以是整数或浮点数。为了定义支持多种类型的类型提示,可以使用 typing 模块中的 Union 函数。

首先,导入 typing 模块中的 Union 函数:

from typing import Union

其次,使用 Union 创建一个包含 int 和 float 的联合类型(union type):

def add(x: Union[int, float], y: Union[int, float]) -> Union[int, float]:
    return x + y

以下是完整的代码:

from typing import Union


def add(x: Union[int, float], y: Union[int, float]) -> Union[int, float]:
    return x + y

从 Python 3.10 开始,我们可以使用 X | Y 语法创建联合类型,例如:

def add(x: int | float, y: int | float) -> int | float:
    return x + y

类型别名

Python 允许为数据类型指定别名,然后在类型提示中使用别名。例如:

from typing import Union

number = Union[int, float]


def add(x: number, y: number) -> number:
    return x + y

以上示例中,我们为联合类型 Union[int, float] 指定了一个别名 Number,然后在 add() 函数中使用了该别名。

列表、字典以及集合的类型提示

我们可以使用以下内置类型作为列表、字典以及集合的类型提示:

  • list
  • dict
  • set

如果我们为变量指定了 list 类型提示,然后为它指定一个字典值,将会返回错误:

ratings: list = [1, 2, 3]
ratings = {1: 'Bad', 2: 'average', 3: 'Good'}

静态类型检查返回的错误如下:

app.py:3: error: Incompatible types in assignment (expression has type "Dict[int, str]", variable has type "List[Any]")
Found 1 error in 1 file (checked 1 source file)

我们可以使用 typing 模块中的以下类型别名为列表、字典以及集合中的值指定类型:

类型别 内置类型
List 列表
Tuple 元组
Dict 字典
Set 集合
Frozenset 不可变集合
Sequence 列表、元组以及其他序列数据类型
Mapping 字典(dict)、集合、不可变集合以及其他映射数据类型
ByteString bytes、bytearray以及memoryview

例如,以下代码定义了一个整数列表:

from typing import List

ratings: List[int] = [1, 2, 3]

None 类型

如果函数没有显式的返回值,可以使用 None 作为返回值的类型提示。例如:

def log(message: str) -> None:
    print(message)

总结

  • Python 类型提示和静态类型检查工具可以使得代码更加健壮。
上一篇:Day3 浮点数拓展


下一篇:20220219 java基础02