Python typing类型注解完全指南

125次阅读
没有评论

共计 11642 个字符,预计需要花费 30 分钟才能阅读完成。

本文全面讲解 Python 的类型注解系统,包括基础用法、高级特性、版本演进和最佳实践。


目录

  1. 类型注解简介
  2. 基础类型注解
  3. 容器类型注解
  4. 函数类型注解
  5. 高级类型
  6. 泛型
  7. 协议和结构化子类型
  8. 类型别名
  9. 版本演进
  10. 类型检查工具
  11. 最佳实践

1. 类型注解简介

什么是类型注解?

类型注解(Type Hints)是 Python 3.5 引入的特性,允许在代码中标注变量、函数参数和返回值的类型。

重要: 类型注解是可选的,不影响运行时行为,主要用于:

  • 代码文档化
  • IDE 智能提示
  • 静态类型检查(mypy 等工具)
  • 提高代码可维护性
# 没有类型注解
def greet(name):
    return f"Hello, {name}"

# 有类型注解
def greet(name: str) -> str:
    return f"Hello, {name}"

为什么使用类型注解?

# 优势 1:IDE 智能提示
def calculate_area(radius: float) -> float:
    return 3.14 * radius ** 2

# IDE 会提示 radius 应该是 float 类型
result = calculate_area(5.0)  # ✓
# result = calculate_area("5")  # IDE 会警告

# 优势 2:提前发现错误
def process_items(items: list[str]) -> int:
    return len(items)

# 优势 3:代码文档化
def fetch_user(user_id: int) -> dict[str, str]:
    """
    通过类型注解就能知道:- user_id 是整数
    - 返回字典,键和值都是字符串
    """return {"name":"Alice","email":"alice@example.com"}

2. 基础类型注解

内置类型

# 基本类型
age: int = 25
price: float = 19.99
name: str = "Alice"
is_active: bool = True
data: bytes = b"hello"

# None 类型
result: None = None

# Any 类型(任意类型)from typing import Any
value: Any = "can be anything"
value = 123  # 也可以 

变量注解

# Python 3.6+:变量注解
count: int
count = 10

# 可以不立即赋值
name: str
# ... 稍后赋值
name = "Bob"

# 类属性注解
class User:
    name: str
    age: int

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

3. 容器类型注解

List、Dict、Set、Tuple

from typing import List, Dict, Set, Tuple

# Python 3.9 之前
names: List[str] = ["Alice", "Bob"]
scores: Dict[str, int] = {"Alice": 95, "Bob": 87}
tags: Set[int] = {1, 2, 3}
point: Tuple[int, int] = (10, 20)

# Python 3.9+:使用内置类型(推荐)names: list[str] = ["Alice", "Bob"]
scores: dict[str, int] = {"Alice": 95, "Bob": 87}
tags: set[int] = {1, 2, 3}
point: tuple[int, int] = (10, 20)

嵌套容器

# 嵌套列表
matrix: list[list[int]] = [[1, 2], [3, 4]]

# 复杂字典
user_data: dict[str, list[int]] = {"Alice": [95, 87, 92],
    "Bob": [78, 85, 90]
}

# 字典的字典
config: dict[str, dict[str, str]] = {"database": {"host": "localhost", "port": "5432"},
    "cache": {"host": "localhost", "port": "6379"}
}

可变长度元组

# 固定长度元组
point: tuple[int, int] = (10, 20)

# 可变长度元组
numbers: tuple[int, ...] = (1, 2, 3, 4, 5)

# 空元组
empty: tuple[()] = ()

4. 函数类型注解

基本函数注解

# 参数和返回值注解
def add(a: int, b: int) -> int:
    return a + b

# 无返回值
def log_message(message: str) -> None:
    print(message)

# 多个参数
def create_user(name: str, age: int, email: str) -> dict[str, str | int]:
    return {"name": name, "age": age, "email": email}

默认参数

def greet(name: str, greeting: str = "Hello") -> str:
    return f"{greeting}, {name}"

# 可选参数
from typing import Optional

def find_user(user_id: int) -> Optional[dict]:
    # 可能返回 dict 或 None
    if user_id > 0:
        return {"id": user_id, "name": "Alice"}
    return None

可变参数

# *args 和 **kwargs
def sum_all(*args: int) -> int:
    return sum(args)

def print_info(**kwargs: str) -> None:
    for key, value in kwargs.items():
        print(f"{key}: {value}")

# 混合使用
def process(
    required: str,
    *args: int,
    optional: str = "default",
    **kwargs: str
) -> None:
    pass

函数类型

from typing import Callable

# 函数作为参数
def apply_operation(x: int, y: int, op: Callable[[int, int], int]) -> int:
    return op(x, y)

def add(a: int, b: int) -> int:
    return a + b

result = apply_operation(5, 3, add)

# 无参数函数
def run_callback(callback: Callable[[], None]) -> None:
    callback()

# 任意参数函数
from typing import Any
def run_function(func: Callable[..., Any]) -> Any:
    return func()

5. 高级类型

Union 类型

from typing import Union

# Union:多种类型之一
def process_id(id: Union[int, str]) -> str:
    return str(id)

process_id(123)      # ✓
process_id("abc")    # ✓

# Python 3.10+:使用 | 运算符(推荐)def process_id(id: int | str) -> str:
    return str(id)

Optional 类型

from typing import Optional

# Optional[X] 等价于 Union[X, None]
def find_user(user_id: int) -> Optional[dict]:
    if user_id > 0:
        return {"id": user_id}
    return None

# Python 3.10+
def find_user(user_id: int) -> dict | None:
    if user_id > 0:
        return {"id": user_id}
    return None

Literal 类型

from typing import Literal

# 限制为特定值
def set_mode(mode: Literal["read", "write", "append"]) -> None:
    print(f"Mode: {mode}")

set_mode("read")    # ✓
# set_mode("delete")  # 类型检查错误

# 多个字面量
Status = Literal["pending", "approved", "rejected"]

def update_status(status: Status) -> None:
    pass

Final 类型

from typing import Final

# 常量(不应被修改)MAX_SIZE: Final = 100
API_KEY: Final[str] = "secret-key"

# MAX_SIZE = 200  # 类型检查器会警告

# 类中的 Final
class Config:
    MAX_RETRIES: Final = 3

    def __init__(self):
        self.timeout: Final = 30  # 实例常量 

TypedDict

from typing import TypedDict

# 定义字典结构
class User(TypedDict):
    name: str
    age: int
    email: str

user: User = {
    "name": "Alice",
    "age": 25,
    "email": "alice@example.com"
}

# 可选字段
class UserOptional(TypedDict, total=False):
    name: str
    age: int
    email: str  # 可选

# Python 3.11+:Required 和 NotRequired
from typing import Required, NotRequired

class UserMixed(TypedDict):
    name: Required[str]      # 必需
    age: NotRequired[int]    # 可选 

NewType

from typing import NewType

# 创建新类型(用于区分相同基础类型)UserId = NewType('UserId', int)
ProductId = NewType('ProductId', int)

def get_user(user_id: UserId) -> dict:
    return {"id": user_id}

def get_product(product_id: ProductId) -> dict:
    return {"id": product_id}

user_id = UserId(123)
product_id = ProductId(456)

get_user(user_id)        # ✓
# get_user(product_id)   # 类型检查错误
# get_user(123)          # 类型检查错误 

6. 泛型

泛型函数

from typing import TypeVar, List

# 定义类型变量
T = TypeVar('T')

def first(items: list[T]) -> T:
    return items[0]

# 自动推断类型
numbers = [1, 2, 3]
first_num = first(numbers)  # 推断为 int

names = ["Alice", "Bob"]
first_name = first(names)   # 推断为 str

新写法 (Python 3.12+)

# 1. 直接写 [T],意思就是:我要定义一个泛型 T
def first[T](items: list[T]) -> T:
    return items[0]

泛型类

from typing import Generic, TypeVar

T = TypeVar('T')

class Stack(Generic[T]):
    def __init__(self):
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        return self._items.pop()

# 使用泛型类
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)

str_stack: Stack[str] = Stack()
str_stack.push("hello")

新写法

class Box[T]:
    def __init__(self, item: T):
        self.item = item

    def get(self) -> T:
        return self.item

# 使用
box1 = Box(123)      # T 推断为 int
box2 = Box("Hello")  # T 推断为 str

约束泛型

from typing import TypeVar

# 约束为特定类型
T = TypeVar('T', int, float)

def add(a: T, b: T) -> T:
    return a + b

add(1, 2)      # ✓ int
add(1.5, 2.5)  # ✓ float
# add("a", "b")  # 错误

# 约束为某个基类的子类
from typing import TypeVar

class Animal:
    pass

class Dog(Animal):
    pass

T = TypeVar('T', bound=Animal)

def process_animal(animal: T) -> T:
    return animal

process_animal(Dog())  # ✓
# process_animal("not an animal")  # 错误 

7. 协议和结构化子类型

Protocol(Python 3.8+)

from typing import Protocol

# 定义协议
class Drawable(Protocol):
    def draw(self) -> None:
        ...

# 任何实现了 draw 方法的类都满足协议
class Circle:
    def draw(self) -> None:
        print("Drawing circle")

class Square:
    def draw(self) -> None:
        print("Drawing square")

def render(shape: Drawable) -> None:
    shape.draw()

render(Circle())  # ✓
render(Square())  # ✓

运行时可检查的协议

from typing import Protocol, runtime_checkable

@runtime_checkable
class Closeable(Protocol):
    def close(self) -> None:
        ...

class File:
    def close(self) -> None:
        print("Closing file")

f = File()
print(isinstance(f, Closeable))  # True

8. 类型别名

简单类型别名

# 类型别名
Vector = list[float]

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

# 复杂类型别名
JSON = dict[str, Any]
Headers = dict[str, str]
Callback = Callable[[int], None]

TypeAlias(Python 3.10+)

from typing import TypeAlias

# 显式声明类型别名
Vector: TypeAlias = list[float]
JSON: TypeAlias = dict[str, Any]

9. 版本演进

Python 3.5(PEP 484)

  • 引入 typing 模块
  • 基础类型注解
  • List, Dict, Set, Tuple 等
from typing import List, Dict

def process(items: List[str]) -> Dict[str, int]:
    return {item: len(item) for item in items}

Python 3.6(PEP 526)

  • 变量注解语法
# 变量注解
name: str = "Alice"
count: int

class User:
    name: str
    age: int

Python 3.7

  • 延迟注解评估(from __future__ import annotations
from __future__ import annotations

class Node:
    def __init__(self, value: int, next: Node | None = None):
        self.value = value
        self.next = next  # 可以引用自身 

Python 3.8(PEP 544)

  • Protocol 协议
  • Literal 类型
  • Final 类型
  • TypedDict
from typing import Protocol, Literal, Final, TypedDict

class Drawable(Protocol):
    def draw(self) -> None: ...

Mode = Literal["r", "w", "a"]
MAX_SIZE: Final = 100

class User(TypedDict):
    name: str
    age: int

Python 3.9(PEP 585)

  • 使用内置类型进行泛型注解(不需要 typing 模块)
# Python 3.9+:直接使用内置类型
def process(items: list[str]) -> dict[str, int]:
    return {item: len(item) for item in items}

# 不再需要
from typing import List, Dict

Python 3.10(PEP 604, 612, 613)

  • Union 类型使用 | 运算符
  • TypeAlias
  • ParamSpec
# Union 使用 |
def process(value: int | str) -> str:
    return str(value)

# TypeAlias
from typing import TypeAlias
Vector: TypeAlias = list[float]

# ParamSpec(高级)from typing import ParamSpec, Callable
P = ParamSpec('P')

def decorator(func: Callable[P, int]) -> Callable[P, int]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> int:
        return func(*args, **kwargs)
    return wrapper

Python 3.11

  • Self 类型
  • TypeVarTuple
  • Required 和 NotRequired
from typing import Self

class Builder:
    def set_name(self, name: str) -> Self:
        self.name = name
        return self

# Required 和 NotRequired
from typing import TypedDict, Required, NotRequired

class User(TypedDict):
    name: Required[str]
    age: NotRequired[int]

Python 3.12

  • 泛型语法改进
  • type 语句
# 新的泛型语法
def first[T](items: list[T]) -> T:
    return items[0]

class Stack[T]:
    def __init__(self):
        self._items: list[T] = []

# type 语句
type Vector = list[float]
type Matrix = list[Vector]

10. 类型检查工具

mypy

最流行的 Python 静态类型检查器。

# 安装
pip install mypy

# 检查文件
mypy script.py

# 检查目录
mypy src/

配置文件(mypy.ini):

[mypy]
python_version = 3.11
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True

pyright

微软开发的类型检查器,速度快。

# 安装
pip install pyright

# 检查
pyright

pyre

Facebook 开发的类型检查器。

# 安装
pip install pyre-check

# 初始化
pyre init

# 检查
pyre check

11. 最佳实践

1. 渐进式类型注解

# 从关键函数开始
def calculate_total(items: list[dict]) -> float:
    return sum(item['price'] for item in items)

# 逐步添加更详细的类型
from typing import TypedDict

class Item(TypedDict):
    name: str
    price: float
    quantity: int

def calculate_total(items: list[Item]) -> float:
    return sum(item['price'] * item['quantity'] for item in items)

2. 使用类型别名提高可读性

# 不好
def process(data: dict[str, list[tuple[int, str]]]) -> list[dict[str, int]]:
    pass

# 好
UserId = int
UserName = str
UserData = tuple[UserId, UserName]
UserMap = dict[str, list[UserData]]
Result = list[dict[str, int]]

def process(data: UserMap) -> Result:
    pass

3. 避免过度使用 Any

from typing import Any

# 不好
def process(data: Any) -> Any:
    return data

# 好:尽可能具体
def process(data: dict[str, int]) -> list[int]:
    return list(data.values())

# 如果真的不确定,使用泛型
from typing import TypeVar
T = TypeVar('T')

def identity(value: T) -> T:
    return value

4. 使用 Protocol 而非 ABC

# 不够灵活
from abc import ABC, abstractmethod

class Drawable(ABC):
    @abstractmethod
    def draw(self) -> None:
        pass

# 更灵活:使用 Protocol
from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None:
        ...

# 任何有 draw 方法的类都自动满足
class Circle:
    def draw(self) -> None:
        print("Drawing circle")

5. 使用 TypedDict 而非普通 dict

# 不好
def create_user(name: str, age: int) -> dict:
    return {"name": name, "age": age}

# 好
from typing import TypedDict

class User(TypedDict):
    name: str
    age: int

def create_user(name: str, age: int) -> User:
    return {"name": name, "age": age}

6. 处理向后兼容

# Python 3.7-3.8 兼容
from __future__ import annotations
from typing import Optional

def process(value: int | None) -> str:  # 使用 | 语法
    return str(value) if value else "None"

# 或者使用 typing_extensions
from typing_extensions import Literal

Mode = Literal["read", "write"]

7. 文档字符串与类型注解结合

def calculate_discount(
    price: float,
    discount_rate: float,
    min_price: float = 0.0
) -> float:
    """
    计算折扣后的价格。Args:
        price: 原价
        discount_rate: 折扣率(0- 1 之间)min_price: 最低价格

    Returns:
        折扣后的价格

    Raises:
        ValueError: 如果 discount_rate 不在 0 - 1 之间
    """
    if not 0 <= discount_rate <= 1:
        raise ValueError("折扣率必须在 0 - 1 之间")

    discounted = price * (1 - discount_rate)
    return max(discounted, min_price)

8. 使用 reveal_type 调试类型

# 使用 mypy 的 reveal_type 查看推断的类型
def process(items: list[int]) -> None:
    reveal_type(items)  # mypy 会显示:list[int]
    first = items[0]
    reveal_type(first)  # mypy 会显示:int

12. 常见模式和技巧

工厂函数

from typing import TypeVar, Type

T = TypeVar('T')

def create_instance(cls: Type[T], *args, **kwargs) -> T:
    return cls(*args, **kwargs)

class User:
    def __init__(self, name: str):
        self.name = name

user = create_instance(User, "Alice")  # 类型推断为 User

装饰器类型注解

from typing import Callable, TypeVar, ParamSpec

P = ParamSpec('P')
R = TypeVar('R')

def log_calls(func: Callable[P, R]) -> Callable[P, R]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_calls
def add(a: int, b: int) -> int:
    return a + b

上下文管理器

from typing import ContextManager
from contextlib import contextmanager

@contextmanager
def open_file(filename: str) -> ContextManager[str]:
    f = open(filename)
    try:
        yield f.read()
    finally:
        f.close()

异步函数

from typing import Coroutine, Any

async def fetch_data(url: str) -> dict[str, Any]:
    # 模拟异步操作
    return {"data": "example"}

# 返回类型是 Coroutine
def get_fetcher() -> Coroutine[Any, Any, dict[str, Any]]:
    return fetch_data("http://example.com")

13. 总结

核心要点

  1. 类型注解是可选的 :不影响运行时,主要用于文档和静态检查
  2. 渐进式采用 :从关键函数开始,逐步完善
  3. 版本差异 :Python 3.9+ 可以直接使用内置类型,3.10+ 支持 | 语法
  4. 工具支持 :使用 mypy、pyright 等工具进行类型检查
  5. 最佳实践 :使用类型别名、Protocol、TypedDict 提高代码质量

版本选择建议

  • Python 3.9+:使用内置类型(list, dict 等)
  • Python 3.10+:使用 | 运算符替代 Union
  • Python 3.11+:使用 Self、Required/NotRequired
  • Python 3.12+:使用新的泛型语法

学习路径

  1. 基础 :基本类型注解、函数注解
  2. 进阶 :容器类型、Optional、Union
  3. 高级 :泛型、Protocol、TypedDict
  4. 专家 :ParamSpec、Concatenate、高级泛型

参考资源


结语

类型注解是 Python 现代化的重要特性,虽然是可选的,但能显著提高代码质量和可维护性。建议在新项目中积极使用,在旧项目中渐进式引入。随着 Python 版本的演进,类型系统越来越强大和易用,值得深入学习和实践。

正文完
 0
nwnusun
版权声明:本站原创文章,由 nwnusun 于2026-02-12发表,共计11642字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)