跳到主要内容

Python 类型注解

类型注解(Type Hints)是 Python 3.5 引入的功能,可以为变量、函数参数和返回值添加类型信息。

为什么要使用类型注解?

  1. 提高代码可读性:明确函数的输入输出类型
  2. 静态类型检查:配合 mypy 等工具进行类型检查
  3. IDE 支持:提供更好的代码补全和错误提示
  4. 文档作用:作为代码的文档说明

基本类型注解

变量注解

# Python 3.6+
name: str = "张三"
age: int = 20
price: float = 19.99
is_active: bool = True

# 列表
numbers: list[int] = [1, 2, 3] # Python 3.9+
# 或 (Python 3.5-3.8)
from typing import List
numbers: List[int] = [1, 2, 3]

# 字典
person: dict[str, int] = {"age": 20} # Python 3.9+
# 或
from typing import Dict
person: Dict[str, int] = {"age": 20}

函数注解

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

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

# 无返回值
def print_hello(name: str) -> None:
print(f"Hello, {name}")

类属性注解

class Person:
name: str
age: int

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

高级类型

Union 类型

from typing import Union

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

# Python 3.10+ 可以使用 | 语法
def process(value: int | str) -> str:
return str(value)

Optional 类型

from typing import Optional

# 可能为 None
def find_user(user_id: int) -> Optional[str]:
if user_id > 0:
return "张三"
return None

# Python 3.10+ 等效写法
def find_user(user_id: int) -> str | None:
if user_id > 0:
return "张三"
return None

Any 类型

from typing import Any

# 任意类型
def log_value(value: Any) -> None:
print(value)

# Any 表示无约束,任何类型都可以
def flexible_func(x: Any) -> Any:
return x

Callable 类型

from typing import Callable

# 可调用对象(函数)
def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
return func(a, b)

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

result = apply(add, 3, 5) # 8

# 无返回值的回调
def on_click(callback: Callable[[], None]) -> None:
callback()

Tuple 类型

from typing import Tuple

# 固定长度的元组
point: Tuple[int, int] = (10, 20)
person: Tuple[str, int, str] = ("张三", 20, "北京")

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

Literal 类型

from typing import Literal

# 限制特定值
def move(direction: Literal["left", "right", "up", "down"]) -> None:
print(f"Moving {direction}")

# 限制特定值和类型
def set_status(status: Literal["active", "inactive", "pending"]) -> None:
print(f"Status: {status}")

泛型(Generics)

TypeVar

from typing import TypeVar, Generic

T = TypeVar('T')

def first_element(lst: list[T]) -> T | None:
"""返回列表的第一个元素"""
return lst[0] if lst else None

# 使用
numbers = [1, 2, 3]
result: int = first_element(numbers)

strings = ["a", "b", "c"]
result: str = first_element(strings)

Generic 类

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()

def peek(self) -> T:
return self._items[-1]

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

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

泛型函数

from typing import TypeVar, Generic

T = TypeVar('T')

def reverse_list(lst: list[T]) -> list[T]:
"""反转列表"""
return lst[::-1]

def find_max(lst: list[T]) -> T | None:
"""找到最大值(需要T可比较)"""
if not lst:
return None
return max(lst)

Protocol(结构化子类型)

runtime_checkable

from typing import Protocol, runtime_checkable

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

class Number:
def __init__(self, value: int):
self.value = value

def __lt__(self, other: "Number") -> bool:
return self.value < other.value

n1 = Number(1)
n2 = Number(2)
print(isinstance(n1, Comparable)) # True

Python 3.12+ 新特性(PEP 695)

Python 3.12 引入了更简洁的泛型语法,无需导入 TypeVar 和 Generic。

泛型函数(新语法)

# Python 3.12+ 新语法 - 无需导入 TypeVar
def first[T](elements: list[T]) -> T | None:
"""返回列表的第一个元素"""
return elements[0] if elements else None

# 使用
numbers = [1, 2, 3]
result: int = first(numbers)

strings = ["a", "b", "c"]
result: str = first(strings)

与旧语法对比:

# 旧语法(仍然可用)
from typing import TypeVar

T = TypeVar('T')

def first_old(elements: list[T]) -> T | None:
return elements[0] if elements else None

泛型类(新语法)

# Python 3.12+ 新语法
class Stack[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)
print(int_stack.pop()) # 2

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

与旧语法对比:

# 旧语法
from typing import Generic, TypeVar

T = TypeVar('T')

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

类型约束和边界

# 约束类型参数只能是特定类型
def process[T: (int, float)](value: T) -> T:
return value

# 使用边界限制类型
class Container[T: str]:
def __init__(self, value: T):
self.value = value

# 泛型类实例
container: Container[str] = Container("hello")

type 语句(类型别名)

Python 3.12 引入了 type 语句来声明类型别名:

# 简单类型别名
type Point = tuple[float, float]
type UserId = int
type Url = str

# 使用
def get_user(user_id: UserId) -> dict[str, str]:
return {"id": str(user_id), "name": "张三"}

# 泛型类型别名
type Vector[T] = list[tuple[T, T]]
type Result[T] = list[T] | tuple[T, str]

# 使用泛型类型别名
coordinates: Vector[float] = [(1.0, 2.0), (3.0, 4.0)]

与旧语法对比:

# 旧语法
from typing import TypeAlias

Point: TypeAlias = tuple[float, float]
Vector_old: TypeAlias = list[tuple[T, T]] # 需要 TypeVar

多类型参数

# 多个类型参数
def pair_to_tuple[T, U](a: T, b: U) -> tuple[T, U]:
return (a, b)

# 使用
result = pair_to_tuple("hello", 42) # tuple[str, int]

# 泛型类多参数
class Map[K, V]:
def __init__(self):
self._data: dict[K, V] = {}

def put(self, key: K, value: V) -> None:
self._data[key] = value

def get(self, key: K) -> V | None:
return self._data.get(key)

# 使用
str_int_map: Map[str, int] = Map()
str_int_map.put("one", 1)
print(str_int_map.get("one")) # 1

PEP 698:@override 装饰器

Python 3.12 引入了 @override 装饰器,用于标记子类中覆盖父类的方法:

from typing import override

class Animal:
def speak(self) -> str:
raise NotImplementedError

class Dog(Animal):
@override
def speak(self) -> str:
return "Woof!"

class Cat(Animal):
@override
def speak(self) -> str:
return "Meow!"

# 使用
dog = Dog()
print(dog.speak()) # Woof!

# 如果方法签名不匹配,类型检查器会报错
class InvalidOverride(Animal):
@override
def speak(self, sound: str) -> str: # 类型检查器会报错
return sound

@override 的作用:

  • 明确标记方法是覆盖父类的方法
  • 类型检查器会验证该方法确实存在于父类中
  • 提高代码可读性和维护性
  • 减少因方法签名不匹配导致的 bug

PEP 692:TypedDict 注解 **kwargs

使用 TypedDict 更精确地注解 **kwargs

from typing import TypedDict, Unpack

class Movie(TypedDict):
name: str
year: int

class Config(TypedDict, total=False):
host: str
port: int

def foo(movie: Unpack[Movie]) -> None:
print(movie)

# 使用
foo(name="Inception", year=2010)

def bar(config: Unpack[Config]) -> None:
print(config)

bar(host="localhost")
bar(host="localhost", port=8080)

这种方式可以精确地为每个 keyword 参数指定类型,而不是像之前那样只能使用统一的类型。

TypedDict

from typing import TypedDict, NotRequired

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

class Config(TypedDict):
host: str
port: int
debug: bool
secret_key: NotRequired[str] # 可选字段

# 使用
person: Person = {"name": "张三", "age": 20}
config: Config = {"host": "localhost", "port": 8080, "debug": True}

特殊类型

NoReturn

from typing import NoReturn

def error_exit(message: str) -> NoReturn:
print(f"Error: {message}")
exit(1)

def process(data: str) -> None:
if not data:
error_exit("Data is empty")
# 后面不会执行

Never(Python 3.11+)

# Never 表示永远不会返回(类似 NoReturn)
def infinite_loop() -> Never:
while True:
pass

Type 类型

from typing import Type

def create_instance(cls: Type[object]) -> object:
return cls()

class MyClass:
pass

obj = create_instance(MyClass)

类型 narrowing

TypeGuard

from typing import TypeGuard

def is_string_list(val: list[object]) -> TypeGuard[list[str]]:
"""缩小类型范围"""
return all(isinstance(x, str) for x in val)

items: list[object] = ["a", "b", "c"]
if is_string_list(items):
# 这里 items 被缩小为 list[str]
print(items[0].upper()) # OK

TypeIs(Python 3.13+)

def is_positive(n: int) -> TypeIs[int]:
"""返回正数的类型"""
return n > 0

n: int = 5
if is_positive(n):
# n 在这里被缩小为正数
print(n + 1) # OK

类型注解最佳实践

1. 逐步添加类型

# 先不加类型
def process(data):
return data.get("value", 0)

# 逐步添加
def process(data: dict[str, int]) -> int:
return data.get("value", 0)

2. 使用类型别名

from typing import Callable

# 清晰的类型别名
UserId = int
UserName = str
UserCallback = Callable[[UserId, UserName], None]

def register(callback: UserCallback) -> None:
callback(1, "张三")

3. 避免过度类型注解

# 不需要注解的情况
name = "张三" # mypy 可以推断
count = len(items) # 明显

# 需要注解的情况
from typing import Optional
result = data.get("key") # 需要注解 Optional[str]

4. 使用 mypy 检查

pip install mypy
mypy your_file.py

typing 模块常用类型

类型说明
int, str, float, bool基础类型
list[T], dict[K, V]容器类型
Optional[T]可能为 None
Union[A, B]多种类型之一
Callable[[args], return]可调用对象
Type[T]类型本身
Any任意类型
None空值

小结

本章我们学习了:

  1. 基本的类型注解(变量、函数参数、返回值)
  2. 高级类型(Union、Optional、Callable、Tuple、Literal)
  3. 泛型(TypeVar、Generic)
  4. Protocol 协议
  5. TypedDict 字典类型
  6. 类型 narrowing(TypeGuard、TypeIs)
  7. 类型检查最佳实践

练习

  1. 为一个学生信息管理系统添加类型注解
  2. 创建一个泛型缓存类
  3. 使用 Protocol 定义一个可序列化协议
  4. 使用 TypedDict 定义 API 响应类型