类型注解基础
Python 的类型系统是可选的、渐进的:代码不强制使用类型注解,已有代码可以逐步添加。类型注解帮助工具(mypy、pyright)在运行前发现错误,也让代码意图更清晰。
为什么需要类型注解
动态类型的问题:
python
def add(a, b):
return a + b
result = add(5, 10) # 15
result = add("Hello, ", "world") # "Hello, world"
result = add(5, "10") # TypeError(运行时才发现)类型检查器可以在运行前发现这类错误:
python
def add(a: int, b: int) -> int:
return a + b
result = add(5, "10") # 类型检查器报错:Expected int, got str基本类型注解
简单类型
python
# 变量注解
name: str = "Alice"
age: int = 30
height: float = 1.75
is_student: bool = True
# 函数参数和返回值
def greet(name: str) -> str:
return f"Hello, {name}"
def calculate_area(width: float, height: float) -> float:
return width * height容器类型
Python 3.9+ 可以直接使用内置类型,旧版本需要 typing 模块:
python
# Python 3.9+ (推荐)
numbers: list[int] = [1, 2, 3]
names: dict[str, str] = {"first": "Alice", "last": "Smith"}
unique_ids: set[int] = {1, 2, 3}
coordinates: tuple[float, float] = (10.0, 20.0)
# Python 3.8 及更早(需要 typing)
from typing import List, Dict, Set, Tuple
numbers: List[int] = [1, 2, 3]
names: Dict[str, str] = {"first": "Alice"}
unique_ids: Set[int] = {1, 2, 3}
coordinates: Tuple[float, float] = (10.0, 20.0)固定长度 tuple:
python
# 三个整数的元组
point_3d: tuple[int, int, int] = (1, 2, 3)
# 不同类型
record: tuple[str, int, float] = ("Alice", 30, 1.75)可变长度 tuple:
python
# 任意数量的整数
numbers: tuple[int, ...] = (1, 2, 3, 4, 5)嵌套类型
python
# 字符串列表的列表
matrix: list[list[int]] = [[1, 2], [3, 4]]
# 字典的列表
users: list[dict[str, str]] = [
{"name": "Alice", "email": "alice@example.com"},
{"name": "Bob", "email": "bob@example.com"}
]
# 复杂嵌套
data: dict[str, list[tuple[int, str]]] = {
"group1": [(1, "a"), (2, "b")],
"group2": [(3, "c")]
}可选类型与联合类型
Optional:可能是 None
python
from typing import Optional
# Optional[str] 等价于 str | None
def find_user(user_id: int) -> Optional[str]:
if user_id == 1:
return "Alice"
return None # 可以返回 None
result = find_user(1) # result 的类型是 Optional[str]
if result is not None:
print(result.upper()) # 类型守卫:这里 result 是 strUnion:多种类型之一
Python 3.9 及更早:
python
from typing import Union
def process(value: Union[int, str]) -> str:
if isinstance(value, int):
return f"Number: {value}"
else:
return f"String: {value}"
process(42) # OK
process("hello") # OK
process(3.14) # 类型检查器报错Python 3.10+ 新语法(推荐):
python
def process(value: int | str) -> str:
if isinstance(value, int):
return f"Number: {value}"
else:
return f"String: {value}"
# Optional 的新写法
def find_user(user_id: int) -> str | None:
...类型别名
为复杂类型起别名,提高可读性:
python
# 定义别名
UserId = int
UserName = str
UserData = dict[str, str | int]
def get_user(user_id: UserId) -> UserData:
return {"name": "Alice", "age": 30}
# 复杂嵌套
Vector = list[float]
Matrix = list[Vector]
def multiply(a: Matrix, b: Matrix) -> Matrix:
...明确类型别名(Python 3.10+):
python
from typing import TypeAlias
UserId: TypeAlias = int
UserData: TypeAlias = dict[str, str | int]函数类型
无返回值
python
def log_message(message: str) -> None:
print(message)
# 不返回任何值(或返回 None)注意:-> None 表示函数不返回有意义的值,而不是"没有返回值注解"。
多个返回值
python
def divide(a: int, b: int) -> tuple[int, int]:
"""返回商和余数"""
return a // b, a % b
quotient, remainder = divide(10, 3)默认参数
python
def greet(name: str, greeting: str = "Hello") -> str:
return f"{greeting}, {name}"
greet("Alice") # OK
greet("Bob", "Hi") # OK
greet("Charlie", greeting="Hey") # OK可变参数
python
def sum_numbers(*args: int) -> int:
return sum(args)
sum_numbers(1, 2, 3, 4) # OK
def print_info(**kwargs: str) -> None:
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", city="NYC") # OK类与方法
实例方法
python
class Calculator:
def __init__(self, initial: int = 0) -> None:
self.value: int = initial
def add(self, amount: int) -> None:
self.value += amount
def get_value(self) -> int:
return self.value
calc = Calculator(10)
calc.add(5)
print(calc.get_value()) # 15类方法与静态方法
python
from typing import Self # Python 3.11+
class Person:
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age
@classmethod
def from_birth_year(cls, name: str, birth_year: int) -> Self:
age = 2024 - birth_year
return cls(name, age)
@staticmethod
def is_adult(age: int) -> bool:
return age >= 18
person = Person.from_birth_year("Alice", 1990)
print(Person.is_adult(20)) # TruePython 3.10 及更早(没有 Self):
python
from typing import TypeVar
T = TypeVar('T', bound='Person')
class Person:
@classmethod
def from_birth_year(cls: type[T], name: str, birth_year: int) -> T:
...类型检查实战
示例代码(example.py):
python
def calculate_average(numbers: list[int]) -> float:
return sum(numbers) / len(numbers)
# 正确用法
result = calculate_average([1, 2, 3, 4, 5])
print(result) # 3.0
# 错误用法
result = calculate_average([1, 2, "3", 4]) # 类型错误
result = calculate_average("12345") # 类型错误运行 mypy:
bash
$ mypy example.py
example.py:8: error: List item 2 has incompatible type "str"; expected "int"
example.py:9: error: Argument 1 to "calculate_average" has incompatible type "str"; expected "list[int]"渐进类型
可以逐步为已有代码添加类型:
python
# 完全没有类型注解(动态类型)
def process_data(data):
return [x * 2 for x in data]
# 部分类型注解
def process_data(data) -> list[int]:
return [x * 2 for x in data]
# 完整类型注解
def process_data(data: list[int]) -> list[int]:
return [x * 2 for x in data]Any:逃生舱口
Any 表示任意类型,关闭类型检查:
python
from typing import Any
def process(value: Any) -> Any:
return value.some_method() # 不检查 value 是否有 some_method
data: Any = "hello"
data = 123 # OK,Any 可以是任何类型
data.anything() # 类型检查器不报错使用场景:
- 处理动态数据(JSON、配置文件)
- 与无类型代码交互
- 快速原型开发
注意:过度使用 Any 会失去类型检查的好处。
实际案例:用户管理系统
python
from typing import Optional
class User:
def __init__(self, user_id: int, name: str, email: str) -> None:
self.user_id = user_id
self.name = name
self.email = email
class UserDatabase:
def __init__(self) -> None:
self.users: dict[int, User] = {}
def add_user(self, user: User) -> None:
self.users[user.user_id] = user
def get_user(self, user_id: int) -> Optional[User]:
return self.users.get(user_id)
def list_users(self) -> list[User]:
return list(self.users.values())
# 使用
db = UserDatabase()
db.add_user(User(1, "Alice", "alice@example.com"))
db.add_user(User(2, "Bob", "bob@example.com"))
user = db.get_user(1)
if user is not None:
print(f"{user.name}: {user.email}")
for user in db.list_users():
print(user.name)常见陷阱
可变默认参数
python
# 错误:可变默认参数
def add_item(item: str, items: list[str] = []) -> list[str]:
items.append(item)
return items
# 正确:使用 None
def add_item(item: str, items: list[str] | None = None) -> list[str]:
if items is None:
items = []
items.append(item)
return items容器协变问题
python
def process_numbers(numbers: list[int]) -> None:
print(sum(numbers))
# 错误(运行时可以,类型检查器会警告)
def get_numbers() -> list[int | str]:
return [1, 2, "3"]
process_numbers(get_numbers()) # 类型不兼容小结
- 类型注解是可选的,不影响运行时行为
- Python 3.9+ 直接用
list、dict等,无需typing.List - Python 3.10+ 用
|代替Union - Optional[T] 等价于
T | None - Any 是逃生舱口,应谨慎使用
- 类型别名 提高复杂类型的可读性
下一节学习 泛型类型,它让类型更灵活和可复用。
练习:答案