输入关键词开始搜索

Python 类型标注 — Type Hints

基础标注

# 变量标注
name: str = "Alice"
age: int = 25
scores: list[float] = [95.5, 87.0]
config: dict[str, str] = {"host": "localhost"}

# 函数标注
def greet(name: str, times: int = 1) -> str:
    return f"Hello {name}" * times

# 无返回值
def log(msg: str) -> None: ...

# 多类型
from typing import Union
def parse(data: Union[str, bytes]) -> dict: ...
# Python 3.10+ 简写: str | bytes

容器类型

# list / dict / set / tuple
names: list[str] = []
scores: dict[str, float] = {}
ids: set[int] = {1, 2, 3}
point: tuple[float, float] = (1.0, 2.0)

# 可变长度 tuple
records: tuple[int, ...] = (1, 2, 3, 4)

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

# 迭代器/生成器
from typing import Iterator, Generator
def fib() -> Iterator[int]: ...
def gen() -> Generator[int, None, str]: ...  # yield int, 无 send, return str

Optional / Union

from typing import Optional

# Optional[X] = Union[X, None]
def find(id: int) -> Optional[str]:  # 可能返回 None
    ...

# Python 3.10+
def find(id: int) -> str | None: ...

# 多种类型
def process(val: int | str | bytes) -> str: ...

Any / Callable

from typing import Any, Callable

# Any — 关闭类型检查
def deserialize(data: str) -> Any: ...

# Callable
def apply(fn: Callable[[int, int], int], a: int, b: int) -> int:
    return fn(a, b)

# 参数名标注
Callback = Callable[..., None]      # 任意参数,无返回
Handler = Callable[[str], bool]     # (str) -> bool

TypedDict — 字典结构

from typing import TypedDict

class User(TypedDict):
    name: str
    age: int
    email: str | None  # 可选

def get_user(id: int) -> User:
    return {"name": "Alice", "age": 25, "email": None}

# 部分可选
class Config(TypedDict, total=False):
    host: str
    port: int
# 所有字段都是可选的

Protocol — 结构化子类型

from typing import Protocol

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

def render(obj: Drawable) -> None:
    obj.draw()  # 不需要继承,只要有 draw 方法即可

# 鸭子类型 + 类型安全
class Circle:
    def draw(self) -> None: print("○")
class Square:
    def draw(self) -> None: print("□")

render(Circle())  # ✅
render(Square())  # ✅

类型别名与 NewType

# 类型别名
UserId = int
JsonDict = dict[str, Any]
Vector = list[float]

# NewType — 编译期区分的类型(运行时仍是 int)
from typing import NewType
UserId = NewType("UserId", int)
OrderId = NewType("OrderId", int)

def get_user(id: UserId) -> User: ...
def get_order(id: OrderId) -> Order: ...

uid = UserId(1)
oid = OrderId(1)
get_user(uid)  # ✅
get_user(oid)  # ❌ mypy 报错(OrderId ≠ UserId)

dataclass — 数据类

from dataclasses import dataclass, field

@dataclass
class Point:
    x: float
    y: float
    label: str = ""          # 默认值

@dataclass
class Config:
    host: str = "localhost"
    port: int = 8080
    tags: list[str] = field(default_factory=list)  # 可变默认值

p1 = Point(1.0, 2.0, "A")
p2 = Point(1.0, 2.0, "A")
assert p1 == p2  # ✅ 自动生成 __eq__

运行时类型检查

# mypy 静态检查
pip install mypy
mypy src/

# 常用配置(pyproject.toml)
[tool.mypy]
strict = true
disallow_untyped_defs = true
ignore_missing_imports = true  # 第三方库没标注时

# pydantic — 运行时验证
from pydantic import BaseModel
class User(BaseModel):
    name: str
    age: int
User(name="Alice", age="25")  # ❌ ValidationError: age must be int

标注风格建议

# ✅ 所有公共函数加类型标注
def public_api(data: list[dict]) -> Result: ...

# ✅ 内部函数可省略(上下文清晰时)
def _internal_helper(x): ...

# ⚠️ 避免过度标注(类型能从上下文推断时)
name = "Alice"         # 不需要: name: str = "Alice"
scores = [1, 2, 3]     # 不需要: scores: list[int] = [1, 2, 3]

# ✅ 标注返回值(推断不出的地方)
def parse_config() -> dict[str, int]: ...