类型注解

Python 的类型注解是可选的,运行时不强制校验,配合 mypy / pyright 等静态分析工具使用。

基本注解

# 变量
name: str = "Alice"
count: int = 0
ratio: float = 0.5
active: bool = True
 
# 函数参数与返回值
def greet(name: str, times: int = 1) -> str:
    return (f"Hello, {name}!\n") * times
 
# 无返回值
def log(msg: str) -> None:
    print(msg)

typing 模块(Python 3.5–3.8)

from typing import List, Dict, Tuple, Set, Optional, Union, Any
 
def process(items: List[int]) -> Dict[str, int]:
    return {"sum": sum(items)}
 
def find(lst: List[str], idx: int) -> Optional[str]:
    return lst[idx] if 0 <= idx < len(lst) else None
 
def accept_either(val: Union[int, str]) -> str:
    return str(val)
 
# Any 关闭类型检查
def flexible(x: Any) -> Any:
    return x

现代语法(Python 3.9+)

内置类型可直接用于注解,无需从 typing 导入。

# 3.9+:直接用内置类型
def process(items: list[int]) -> dict[str, int]: ...
def coords() -> tuple[float, float]: ...
def unique(items: list[str]) -> set[str]: ...
 
# 3.10+:使用 | 替代 Union
def parse(val: int | str) -> str:
    return str(val)
 
# 3.10+:X | None 替代 Optional[X]
def find(key: str) -> int | None:
    return None

Callable 与高阶函数

from typing import Callable
 
def apply(fn: Callable[[int, int], int], a: int, b: int) -> int:
    return fn(a, b)
 
# 参数数量不定时
def run(callback: Callable[..., None]) -> None:
    callback()

TypeVar — 泛型

from typing import TypeVar, Sequence
 
T = TypeVar("T")
 
def first(items: Sequence[T]) -> T:
    return items[0]
 
first([1, 2, 3])      # int
first(["a", "b"])     # str

TypeAlias

from typing import TypeAlias   # 3.10+
 
Vector: TypeAlias = list[float]
Matrix: TypeAlias = list[list[float]]
 
def dot(a: Vector, b: Vector) -> float:
    return sum(x * y for x, y in zip(a, b))

TypedDict

from typing import TypedDict
 
class Movie(TypedDict):
    title: str
    year: int
    rating: float
 
m: Movie = {"title": "Inception", "year": 2010, "rating": 8.8}

Protocol — 结构化子类型

from typing import Protocol
 
class Drawable(Protocol):
    def draw(self) -> None: ...
 
class Circle:
    def draw(self) -> None:
        print("○")
 
def render(obj: Drawable) -> None:
    obj.draw()
 
render(Circle())   # 无需继承 Drawable,只需有 draw 方法

dataclass 与类型注解

from dataclasses import dataclass, field
from typing import ClassVar
 
@dataclass
class Config:
    host: str
    port: int = 8080
    tags: list[str] = field(default_factory=list)
    _instance_count: ClassVar[int] = 0   # 类变量,不进入 __init__

前向引用

在类体内引用自身类型时,使用字符串或 from __future__ import annotations

from __future__ import annotations   # 推迟所有注解求值(文件顶部)
 
class Node:
    def __init__(self, val: int, next: Node | None = None):
        self.val = val
        self.next = next

运行时访问注解

def add(a: int, b: int) -> int:
    return a + b
 
import inspect
sig = inspect.signature(add)
for name, param in sig.parameters.items():
    print(name, param.annotation)
 
add.__annotations__   # {'a': int, 'b': int, 'return': int}

类型检查工具

# mypy:成熟的静态类型检查器
pip install mypy
mypy script.py
 
# pyright / pylance:微软出品,VS Code 内置
pip install pyright
pyright script.py
 
# pydantic:运行时验证(常用于 API 和配置)
pip install pydantic
from pydantic import BaseModel, Field
 
class User(BaseModel):
    name: str
    age: int = Field(gt=0, le=150)
    email: str
 
u = User(name="Alice", age=30, email="alice@example.com")
u.model_dump()   # {'name': 'Alice', 'age': 30, 'email': '...'}

相关链接