13.Python3 类型注解

Python3 类型注解

Python 类型注解(Type Hints)是自 Python 3.5 引入的一种特性,它允许开发者在定义函数、变量时指定数据类型。类型注解并非强制执行的,它们主要用于提高代码的可读性和可维护性,并支持静态分析工具进行类型检查。类型注解不会影响 Python 的动态特性,也不会在运行时被强制执行,尤其是在大型项目中可以帮助开发者减少错误。

Python 类型注解文档:https://docs.python.org/zh-cn/3.13/library/typing.html

1. 基本语法

类型注解的基本语法包括在变量声明或函数定义中使用冒号 : 来指定参数的类型,并且使用箭头 -> 来指明函数的返回值类型

变量注解:

# 内置类型注解 int, float, bool, str
age: int = 20

# Python 3.9+
items: list[int] = [1, 2, 3]

# Python 3.8及以下版本
from typing import List, Dict, Tuple, Set
# 列表中元素为整数
numbers: List[int] = [1, 2, 3]
# 字典键为字符串,值为浮点数
prices: Dict[str, float] = {"apple": 2.5, "banana": 1.2}
# 元组内有两个元素,分别是字符串和整数
pair: Tuple[str, int] = ("hello", 42)
# 集合中元素为布尔值
flags: Set[bool] = {True, False}

对于内置类型如 int, float, bool, str 等,可以直接使用这些类型作为注解。此外,Python 还提供了对容器类型的支持,比如列表 list、元组 tuple、字典 dict 和集合 set。在 Python 3.9 及以上版本中,可以直接使用标准库中的容器类型进行类型注解;而在更早的版本中,则需要从 typing 模块导入相应的泛型容器类型。

函数注解:

def greet(name: str) -> str:
    return 'Hello, ' + name

这里 name: str 表示期望传入的 name 参数是一个字符串,而 -> str 表示这个函数应该返回一个字符串。

2.复杂类型注解

除了简单的内置类型外,typing 模块还提供了更多复杂的类型构造器来创建复合类型。

1. Union 类型

Union 用来表示一个变量或函数参数可以接受多种类型中的任意一种。例如,如果一个函数既可以接收整数也可以接收字符串作为参数,我们可以这样写:

from typing import Union

def normalize_id(id: Union[int, str]) -> str:
    return str(id)
# 在这个例子中,`normalize_id` 函数的参数 `id` 可以是 `int` 或者 `str` 类型。

2. Optional 类型

Optional即表示一个变量可以是给定类型的值或者是 None。这对于可能为空的返回值或参数非常有用:

from typing import Optional

def get_first_element(lst: list) -> Optional[str]:
    if lst:
        return lst[0]
    else:
        return None
# get_first_element 函数的返回值可能是列表的第一个元素(假设是字符串)或者当列表为空时返回 `None`。

3. Callable 类型

Callable 用来描述一个可调用对象(如函数、方法或类),它指定了该对象接收的参数类型以及返回值类型。这有助于确保传递给其他函数的回调函数具有正确的签名:

from typing import Callable

def invoke_callback(callback: Callable[[int], str], value: int) -> str:
    return callback(value)

# 定义符合上述签名的回调函数
def stringifier(number: int) -> str:
    return f"Number is {number}"

# 调用 invoke_callback 并传入回调函数
result = invoke_callback(stringifier, 42)

# Callable[[Arg1Type, Arg2Type], ReturnType]
# Callable[[参数1类型,参数2类型], 返回值类型]

4. Literal 类型

Literal 用来限制变量只能取某些特定的字面量值。这对于配置选项、枚举值等场景特别有用,因为可以确保只允许预先定义好的几个值:

from typing import Literal

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

set_mode('read')  # 正确
set_mode('execute')  # 错误,在静态分析时会被捕获

5. TypeAlias 类型

TypeAlias 允许为复杂类型创建别名,使得代码更加简洁且易于理解。从 Python 3.11 开始,TypeAlias 成为了正式的一部分,但在此之前可以通过简单的赋值来创建类型别名:

from typing import TypeAlias, List

# Python 3.11前直接赋值创建类型别名
vector = List[float]

# Python 3.11 后使用TypeAlias创建类型别名
Vector: TypeAlias = List[float]

def scale_vector(v: Vector, factor: float) -> Vector:
    return [x * factor for x in v]

vector_example: Vector = [1.0, 2.0, 3.0]
scaled_vector = scale_vector(vector_example, 2.0)

6. Any 类型

当不确定或者不关心具体的类型时,可以使用 Any 类型:

from typing import Any

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

3. 协变与逆变

在 Python 的类型系统中,协变(Covariant)和逆变(Contravariant)用于描述类型之间的关系,特别是在容器类型中。

  • 协变:如果 CatAnimal 的子类,那么 List[Cat] 也可以被视为 List[Animal] 的子类。这种情况下,我们说 List 对其元素类型是协变的。
  • 逆变:对于某些类型的容器,如函数参数,如果我们有一个接受 Animal 类型参数的函数,那么它也能够接受接受 Cat 类型参数的函数作为参数。这里,函数参数位置上的类型是逆变的。

Python 的 typing 模块中,SequenceMapping 等类型默认是协变的,而 Callable 默认是逆变于输入参数且协变于返回值。

4.泛型类

TypeVar 用于创建类型变量,这些变量可以被用作泛型类型的参数。类型变量允许你在定义函数、类或方法时指定某些参数或返回值的类型可以是任意类型,但是调用者必须提供一个具体的类型来替代这个类型变量。TypeVar 主要用于定义泛型函数、类和容器。

from typing import TypeVar, List

T = TypeVar('T')  # 创建一个名为 T 的类型变量

def first_element(lst: List[T]) -> T:
    """Return the first element of a non-empty list."""
    return lst[0] if lst else None

在这个例子中,T 是一个类型变量,它表示 first_element 函数可以接受任何类型的列表,并且返回该列表中元素的相同类型。这意味着如果你传入一个 List[int],那么返回值也将是 int 类型;如果你传入 List[str],则返回值将是 str 类型。

5. NewType

NewType 是 Python 的 typing 模块提供的一个工具,用于创建名义上不同的类型。尽管这些新类型的值实际上与原始类型具有相同的底层表示,但在静态类型检查时,它们被视为完全不同的类型。这有助于提高代码的安全性和可读性,因为它可以防止在不应该的地方互换使用看似相似的类型。NewType 接受两个参数:第一个是新类型的名称,第二个是现有类型的引用。下面是一个简单的例子:

from typing import NewType

UserId = NewType('UserId', int)

def get_user_name(user_id: UserId) -> str:
    # 在这里实现获取用户名的逻辑
    return f"User {user_id}"

# 正确使用
user_id = UserId(42)
print(get_user_name(user_id))

# 错误使用(静态分析会捕捉到这个错误)
# print(get_user_name(42))  # 类型检查器将报告错误

在这个例子中,UserId 被定义为 int 的一种特殊形式。然而,在静态类型检查期间,UserIdint 将被视为不同的类型。因此,直接传递一个整数给 get_user_name 函数会导致类型检查错误,而传递一个 UserId 对象则是正确的。

6. 静态类型检查设置

对于 VSCode,通常只需要确保安装了 Python 扩展,并且启用了类型检查功能。可以在设置中搜索 Python › Analysis: Type Checking Mode 并选择 basicstrict 模式。测试代码:

from typing import NewType

UserId = NewType('UserId', int)

def get_user_name(user_id: UserId) -> str:
    return f"User {user_id}"

# 正确使用
user_id = UserId(42)
print(get_user_name(user_id))

# 错误使用
print(get_user_name(42))  # 类型检查器应报告错误
posted @   littlecamel  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10亿数据,如何做迁移?
· 推荐几款开源且免费的 .NET MAUI 组件库
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示