Python 类型注解
Annotations for readability and tooling, Optional/Union syntax, generics, and dataclasses.
Key PointsHints are not enforced at runtime; they power IDE completion, mypy/pyright checks and FastAPI/Pydantic validation.
Basic Annotations
Python
def greet(name: str, times: int = 1) -> str:
return f"Hello, {name}! " * times
def log(msg: str) -> None:
print(msg)
# 变量注解
count: int = 0
names: list[str] = []
mapping: dict[str, int] = {}Optional and Union
X | None (3.10+) is the modern way to say Optional[X]. Union[A, B] becomes A | B. Runtime does not care — but the checker will catch the None branch you forgot.
Python
def find_user(uid: int) -> dict | None:
return None
def parse(value: str | int) -> int:
return int(value)
# 3.9 及更早:from typing import Optional, UnionGenerics and Callables
Python
from collections.abc import Callable, Iterable, Iterator, Sequence
def first(items: Sequence[int]) -> int:
return items[0]
def apply_all(func: Callable[[int], int], data: Iterable[int]) -> Iterator[int]:
for x in data:
yield func(x)
def index_by(rows: list[dict], key: str) -> dict[str, dict]:
return {r[key]: r for r in rows}Protocol: Structural Typing
Protocol describes behaviour instead of a class hierarchy — any object with matching methods satisfies it. This is duck typing with static checking.
Python
from typing import Protocol
class Readable(Protocol):
def read(self) -> bytes: ...
def load(src: Readable) -> bytes:
return src.read()
# 任何有 read() 的对象都可以传进来:文件、socket、StringIOdataclass
@dataclass generates __init__, __repr__ and __eq__ from the field declarations. frozen=True adds immutability and hashability.
Python
from dataclasses import dataclass, field
@dataclass
class Point:
x: int
y: int
tags: list[str] = field(default_factory=list) # 可变默认值必须用 factory
@dataclass(frozen=True)
class Config:
host: str = "127.0.0.1"
port: int = 8000
p = Point(1, 2)
print(p) # Point(x=1, y=2, tags=[])
print(p == Point(1, 2, [])) # True
print(hash(Config())) # 可哈希Never use a mutable default (
tags: list = []) in a dataclass — Python raises ValueError and tells you to use field(default_factory=...).Checking Types
Bash
pip install mypy
mypy app/ --strict
# 或更快的 pyright / basedpyright基础注解
Python
def greet(name: str, times: int = 1) -> str:
return f"Hello, {name}! " * times
def log(msg: str) -> None:
print(msg)
# 变量注解
count: int = 0
names: list[str] = []
mapping: dict[str, int] = {}Optional 与 Union
X | None(3.10+)是现代写法,等价 Optional[X];Union[A, B] 写成 A | B。运行期不检查 —— 但检查器会抓住你漏掉的 None 分支。
Python
def find_user(uid: int) -> dict | None:
return None
def parse(value: str | int) -> int:
return int(value)
# 3.9 及更早:from typing import Optional, Union泛型与可调用对象
Python
from collections.abc import Callable, Iterable, Iterator, Sequence
def first(items: Sequence[int]) -> int:
return items[0]
def apply_all(func: Callable[[int], int], data: Iterable[int]) -> Iterator[int]:
for x in data:
yield func(x)
def index_by(rows: list[dict], key: str) -> dict[str, dict]:
return {r[key]: r for r in rows}Protocol:结构化类型
Protocol 描述的是行为而非继承关系 —— 任何具备对应方法的对象都算满足。这是带静态检查的鸭子类型。
Python
from typing import Protocol
class Readable(Protocol):
def read(self) -> bytes: ...
def load(src: Readable) -> bytes:
return src.read()
# 任何有 read() 的对象都可以传进来:文件、socket、StringIOdataclass
@dataclass 从字段声明自动生成 __init__、__repr__、__eq__。frozen=True 加上不可变与可哈希。
Python
from dataclasses import dataclass, field
@dataclass
class Point:
x: int
y: int
tags: list[str] = field(default_factory=list) # 可变默认值必须用 factory
@dataclass(frozen=True)
class Config:
host: str = "127.0.0.1"
port: int = 8000
p = Point(1, 2)
print(p) # Point(x=1, y=2, tags=[])
print(p == Point(1, 2, [])) # True
print(hash(Config())) # 可哈希dataclass 里不要用可变默认值(
tags: list = [])—— Python 会直接报 ValueError,要求改用 field(default_factory=...)。类型检查
Bash
pip install mypy
mypy app/ --strict
# 或更快的 pyright / basedpyright