MEMOO
MEMOO
Published on 2025-04-20 / 5 Visits
0
0

Python 类型提示

摘要:在本教程中,你将学习 Python 类型提示以及如何使用 mypy 工具进行静态类型检查。

Python 类型提示介绍

一些编程语言具有静态类型,例如 C/C++。这意味着你需要提前声明变量、参数以及函数返回值的类型。预定义的类型允许编译器在编译和运行程序之前检查代码。

Python 使用动态类型,其中函数的变量、参数和返回值可以是任何类型。此外,变量的类型可以在程序运行时改变。

一般来说,动态类型使得编程变得容易,但会导致一些只有在程序运行时才能发现的意外错误。

Python 的类型提示为你提供了可选的静态类型,以充分利用静态类型和动态类型的优点。

以下示例定义了一个简单的函数,该函数接受一个字符串并返回另一个字符串:

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


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

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

parameter: type
-> type

例如,以下展示了如何为 say_hi() 函数的 name 参数和返回值使用类型提示:

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


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

输出:

Hi John

在这种新语法中name 参数的类型为 str

name: str

并且 say_hi() 函数的返回值也具有 str 类型:

-> str

除了 str 类型,你还可以使用其他内置类型(如 intfloatboolbytes)来进行类型提示。

需要注意的是,Python 解释器会完全忽略类型提示。如果你将一个数字传递给 say_hi() 函数,程序将运行且不会发出任何警告或错误:

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


greeting = say_hi(123)
print(greeting)

输出:

Hi 123

要检查类型提示的语法,你需要使用一个静态类型检查工具。

使用静态类型检查工具:mypy

Python 没有官方的静态类型检查工具。目前,最流行的第三方工具是 Mypy。由于 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 的值是一个字符串字面量,因此静态类型检查器会推断 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)模块为多种类型设置类型提示。

首先,从 typing 模块导入 Union

from typing import Union

第二,使用 Union 创建一个包含 intfloat 的联合类型:

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() 函数中使用了 Number 别名。

为列表、字典和集合添加类型提示

你可以使用以下内置类型为列表、字典和集合设置类型提示:

  • list

  • dict

  • set

如果你在一个变量中类型提示为列表,但后来为其分配了一个字典,你会得到一个错误:

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

list

Tuple

tuple

Dict

dict

Set

set

Frozenset

frozenset

Sequence

对于 list(列表)tuple(元组)以及任何其他序列数据类型。

Mapping

对于字典dict)、集合set)、不可变集合frozenset)以及任何其他映射数据类型。

ByteString

bytesbytearraymemoryview 类型。

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

from typing import List

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

None 类型

如果一个函数没有显式地返回值,你可以使用 None 来对返回值进行类型提示。例如:

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

字面量类型

如果你希望一个变量或函数参数接受特定的字面量值列表,你可以使用 Literal 类型。例如:

from typing import Literal

def set_mode(mode: Literal['read', 'write']) -> None:
    print(f"Setting mode to {mode}")

set_mode('write')

工作原理:

首先,从 typing 模块导入 Literal

from typing import Literal

其次,定义 set_mode 函数,该函数接受一个类型为 Literal 的参数 modemode 只能接受两个字符串字面量之一,即 'read''write'

mode: Literal['read', 'write']

第三,调用 set_mode 函数并传递 'write' 参数:

set_mode('write')

如果你运行 mypy 命令,它将显示输出:

Success: no issues found in 1 source file

如果你将参数更改为不是 'read''write' 的值mypy 将会报错:

set_mode('execute')

错误:

main.py:8: error: Argument 1 to "set_mode" has incompatible type "Literal['execute']"; expected "Literal['read', 'write']"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Final变量

你可以给一个最终变量(final variable)赋值一次。如果你再次给它赋值,将会遇到错误。最终变量有助于防止你意外修改常量。例如:

from typing import Final

HTTPS : Final[bool] = True

print(HTTPS )

工作原理:

首先,从 typing 模块导入 Final

from typing import Final

其次,定义一个类型为 Final[bool] 的常量 HTTPS,并将其值设置为 True

HTTPS : Final[bool] = True

除了 bool 类型,你还可以使用其他内置类型,如 intfloatstr等,来存储各种类型的最终值。

第三,显示 HTTPS 的值:

print(HTTPS)

mypy 会将该程序标记为成功。

如果你尝试将 HTTPS 改为 False,类型检查器mypy会将该赋值操作标记为错误。

from typing import Final

HTTPS : Final[bool] = True
HTTPS = False # error

错误:

main.py:4: error: Cannot assign to final name "HTTPS"  [misc]
Found 1 error in 1 file (checked 1 source file)

总结

  • 使用类型提示(type hints)和静态类型检查工具,使你的代码更加健壮。


Comment