Python类型系统Skill python-type-system

这个技能用于掌握Python的类型系统,包括类型提示、静态类型检查工具mypy、高级类型特性如Protocol、TypedDict和Generics。帮助开发者编写类型安全、可维护的代码,提高代码质量和开发效率。关键词:Python类型系统、类型提示、mypy、Protocol、TypedDict、Generics、静态类型检查、代码安全、维护性、类型注解。

架构设计 0 次安装 0 次浏览 更新于 3/25/2026

名称: python-type-system 用户可调用: false 描述: 当涉及Python的类型系统,包括类型提示、mypy、Protocol、TypedDict和Generics时使用。在处理Python类型注解时使用。 允许工具:

  • Bash
  • Read

Python 类型系统

掌握Python的类型系统以编写类型安全、可维护的代码。此技能涵盖类型提示、使用mypy进行静态类型检查以及高级类型特性。

类型检查工具

# 安装mypy用于静态类型检查
pip install mypy

# 在文件或目录上运行mypy
mypy my_module.py
mypy src/

# 使用特定配置运行
mypy --config-file mypy.ini src/

# 使用严格模式运行
mypy --strict src/

# 显示类型覆盖报告
mypy --html-report mypy-report src/

mypy 配置

mypy.ini 配置文件:

[mypy]
# 全局选项
python_version = 3.11
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
disallow_any_unimported = True
no_implicit_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
warn_no_return = True
check_untyped_defs = True
strict_equality = True

# 每个模块选项
[mypy-tests.*]
disallow_untyped_defs = False

[mypy-third_party.*]
ignore_missing_imports = True

pyproject.toml 配置:

[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_any_unimported = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
check_untyped_defs = true
strict_equality = true

[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false

基本类型提示

原始类型和集合:

from typing import List, Dict, Set, Tuple, Optional, Union, Any

# 基本类型
def greet(name: str) -> str:
    return f"Hello, {name}"

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

# 可选(可以为None)
def find_user(user_id: int) -> Optional[str]:
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

# 联合类型(多种可能类型)
def process_value(value: Union[int, str]) -> str:
    return str(value)

# 带有固定类型的元组
def get_coordinates() -> Tuple[float, float]:
    return (37.7749, -122.4194)

# 任意类型(尽量避免)
def process_data(data: Any) -> None:
    print(data)

现代类型语法(Python 3.10+)

使用 PEP 604 联合语法:

# Python 3.10+ 使用 | 的联合语法
def process_value(value: int | str) -> str:
    return str(value)

# 使用 | None 的可选类型
def find_user(user_id: int) -> str | None:
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

# 多个联合
def handle_response(
    response: dict | list | str | None
) -> str:
    if response is None:
        return "No response"
    return str(response)

内置泛型类型(Python 3.9+):

# 使用内置类型替代 typing 模块
def process_items(items: list[str]) -> dict[str, int]:
    return {item: len(item) for item in items}

def get_mapping() -> dict[str, list[int]]:
    return {"numbers": [1, 2, 3]}

def get_unique(items: list[str]) -> set[str]:
    return set(items)

# 嵌套泛型
def group_items(
    items: list[tuple[str, int]]
) -> dict[str, list[int]]:
    result: dict[str, list[int]] = {}
    for key, value in items:
        result.setdefault(key, []).append(value)
    return result

泛型类型

创建泛型函数和类:

from typing import TypeVar, Generic, Sequence

# 类型变量
T = TypeVar("T")

def first(items: Sequence[T]) -> T | None:
    return items[0] if items else None

def last(items: list[T]) -> T | None:
    return items[-1] if items else None

# 约束类型变量
Number = TypeVar("Number", int, float)

def add(a: Number, b: Number) -> Number:
    return a + b  # type: ignore

# 泛型类
class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []

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

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

    def peek(self) -> T | None:
        return self._items[-1] if self._items else None

# 使用
stack: Stack[int] = Stack()
stack.push(1)
stack.push(2)
value: int = stack.pop()

绑定类型变量:

from typing import TypeVar
from collections.abc import Sized

# 带有上界约束的类型变量
TSized = TypeVar("TSized", bound=Sized)

def get_length(obj: TSized) -> int:
    return len(obj)

# 适用于任何 Sized 类型
get_length("hello")
get_length([1, 2, 3])
get_length({"a": 1})

Protocol(结构子类型)

使用 Protocol 定义接口:

from typing import Protocol

# 定义一个 protocol
class Drawable(Protocol):
    def draw(self) -> str:
        ...

# 匹配 protocol 的类不需要继承
class Circle:
    def draw(self) -> str:
        return "Drawing circle"

class Square:
    def draw(self) -> str:
        return "Drawing square"

# 函数接受任何匹配 protocol 的类型
def render(shape: Drawable) -> None:
    print(shape.draw())

# 适用于任何匹配的类
render(Circle())
render(Square())

带有属性和方法的 Protocol:

from typing import Protocol

class Comparable(Protocol):
    def __lt__(self, other: "Comparable") -> bool:
        ...

    def __gt__(self, other: "Comparable") -> bool:
        ...

def find_max(items: list[Comparable]) -> Comparable:
    return max(items)

class Person:
    def __init__(self, name: str, age: int) -> None:
        self.name = name
        self.age = age

    def __lt__(self, other: "Person") -> bool:
        return self.age < other.age

    def __gt__(self, other: "Person") -> bool:
        return self.age > other.age

# 有效,因为 Person 实现了 Comparable protocol
people = [Person("Alice", 30), Person("Bob", 25)]
oldest = find_max(people)

运行时可检查的 protocols:

from typing import Protocol, runtime_checkable

@runtime_checkable
class Serializable(Protocol):
    def to_dict(self) -> dict[str, Any]:
        ...

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

    def to_dict(self) -> dict[str, Any]:
        return {"name": self.name}

user = User("Alice")
assert isinstance(user, Serializable)

TypedDict

使用 TypedDict 定义字典形状:

from typing import TypedDict, NotRequired

# 基本 TypedDict
class UserDict(TypedDict):
    id: int
    name: str
    email: str

def create_user(user: UserDict) -> UserDict:
    return user

user: UserDict = {
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com"
}

# 可选字段(Python 3.11+)
class PersonDict(TypedDict):
    name: str
    age: int
    email: NotRequired[str]  # 可选字段

person: PersonDict = {"name": "Bob", "age": 30}

# Total=False 使所有字段可选
class ConfigDict(TypedDict, total=False):
    host: str
    port: int
    debug: bool

config: ConfigDict = {"host": "localhost"}

TypedDict 继承:

from typing import TypedDict

class BaseUserDict(TypedDict):
    id: int
    name: str

class ExtendedUserDict(BaseUserDict):
    email: str
    is_active: bool

user: ExtendedUserDict = {
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com",
    "is_active": True
}

字面量类型

限制值为特定字面量:

from typing import Literal

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

# 有效
set_mode("read")

# 类型错误:无效字面量
# set_mode("invalid")

# 字面量联合
Status = Literal["pending", "active", "completed"]

def update_status(status: Status) -> None:
    print(f"Status: {status}")

# 多种类型的字面量
MixedLiteral = Literal[True, 1, "one"]

类型别名

为复杂类型创建类型别名:

from typing import TypeAlias

# 类型别名
UserId: TypeAlias = int
UserName: TypeAlias = str

def get_user(user_id: UserId) -> UserName:
    return f"User {user_id}"

# 复杂类型别名
JsonValue: TypeAlias = (
    dict[str, "JsonValue"]
    | list["JsonValue"]
    | str
    | int
    | float
    | bool
    | None
)

def process_json(data: JsonValue) -> None:
    print(data)

# 泛型类型别名
Vector: TypeAlias = list[float]
Matrix: TypeAlias = list[Vector]

def multiply_matrix(a: Matrix, b: Matrix) -> Matrix:
    # 实现
    return [[0.0]]

Callable 类型

函数和可调用对象的类型提示:

from typing import Callable

# 接受回调的函数
def apply_operation(
    x: int,
    y: int,
    operation: Callable[[int, int], int]
) -> int:
    return operation(x, y)

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

result = apply_operation(5, 3, add)

# 无参数的可调用对象
def execute(task: Callable[[], None]) -> None:
    task()

# 带多种参数类型的回调
Callback: TypeAlias = Callable[[str, int], bool]

def register_handler(callback: Callback) -> None:
    callback("test", 42)

ParamSpec 和 Concatenate

高级可调用类型:

from typing import Callable, ParamSpec, TypeVar, Concatenate
from functools import wraps

P = ParamSpec("P")
R = TypeVar("R")

# 保留函数签名的装饰器
def log_calls(
    func: Callable[P, R]
) -> Callable[P, R]:
    @wraps(func)
    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

# Concatenate 添加参数
def with_context(
    func: Callable[Concatenate[str, P], R]
) -> Callable[P, R]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        return func("context", *args, **kwargs)
    return wrapper

类型守卫

用于运行时类型缩窄的类型守卫:

from typing import TypeGuard

def is_str_list(val: list[object]) -> TypeGuard[list[str]]:
    return all(isinstance(x, str) for x in val)

def process_strings(values: list[object]) -> None:
    if is_str_list(values):
        # 类型缩窄为 list[str]
        for value in values:
            print(value.upper())

# 更复杂的类型守卫
def is_non_empty_str(val: str | None) -> TypeGuard[str]:
    return val is not None and len(val) > 0

def process_name(name: str | None) -> None:
    if is_non_empty_str(name):
        # 类型缩窄为 str(非None)
        print(name.upper())

Overload

使用 overload 定义多个函数签名:

from typing import overload, Literal

@overload
def get_value(key: str, as_int: Literal[True]) -> int:
    ...

@overload
def get_value(key: str, as_int: Literal[False]) -> str:
    ...

def get_value(key: str, as_int: bool) -> int | str:
    value = "42"
    return int(value) if as_int else value

# 类型检查器基于字面量知道返回类型
int_value: int = get_value("key", True)
str_value: str = get_value("key", False)

常见模式

避免常见的类型检查问题:

from typing import TYPE_CHECKING, cast

# 避免循环导入
if TYPE_CHECKING:
    from my_module import MyClass

def process(obj: "MyClass") -> None:
    pass

# 当您比类型检查器更了解时进行类型转换
def get_data() -> object:
    return {"key": "value"}

data = cast(dict[str, str], get_data())

# 使用 reveal_type 断言类型(仅 mypy)
x = [1, 2, 3]
# reveal_type(x)  # 显示: list[int]

# 忽略特定行的类型检查
result = some_untyped_function()  # type: ignore[no-untyped-call]

# 忽略特定错误代码
value: Any = get_dynamic_value()
processed = process_value(value)  # type: ignore[arg-type]

何时使用此技能

使用 python-type-system 当您需要时:

  • 为Python代码添加类型提示以改善IDE支持和文档
  • 为项目配置 mypy 进行静态类型检查
  • 创建可重用的泛型函数和类
  • 使用 Protocol 定义结构接口
  • 使用 TypedDict 指定确切的字典形状
  • 使用 ParamSpec 创建类型安全的装饰器
  • 使用 TypeGuard 实现运行时类型缩窄
  • 处理复杂的联合类型和字面量类型
  • 构建类型安全的API和库接口

最佳实践

  • 在 mypy 中启用严格模式以最大化类型安全
  • 可能时使用 Protocol 替代 ABC 进行结构类型化
  • 优先使用内置泛型类型(list、dict)而非 typing 模块(3.9+)
  • 使用 TypedDict 替代 Dict[str, Any] 用于字典形状
  • 为复杂类型创建类型别名以提高可读性
  • 使用 TYPE_CHECKING 避免循环导入问题
  • 逐步添加类型提示,从公共API开始
  • 在 CI/CD 中运行 mypy 以尽早捕获类型错误
  • 在开发中使用 reveal_type 调试类型推断
  • 除非与未类型化代码交互,否则避免使用 Any 类型

常见陷阱

  • 忘记处理 Optional 类型中的 None
  • 使用可变默认参数(使用 None 并在函数中创建)
  • 未为鸭子类型接口使用 Protocol
  • 过度使用 Any 类型,降低类型安全益处
  • 在 mypy 配置中未启用严格模式
  • 忽略类型错误而不是正确修复
  • 在 Python 3.9+ 中使用旧的 typing 语法(List、Dict)
  • 循环导入问题与正向引用
  • 不理解泛型类型中的方差
  • 混用运行时行为与类型提示(使用 TypeGuard)

资源