Skip to content

类型注解基础 ​

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 是 str

Union:多种类型之一 ​

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))  # True

Python 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 是逃生舱口,应谨慎使用
  • 类型别名 提高复杂类型的可读性

下一节学习 泛型类型,它让类型更灵活和可复用。


参考:PEP 484 – Type Hints

练习:答案

用心记录每一步成长