类型注解与类型检查

一句话概述

类型注解(Type Hints)是 Python 3.5+ 引入的可选语法,允许在函数参数、返回值、变量上标注期望的类型——def greet(name: str) -> str:。Python 运行时不会强制检查这些注解(鸭子类型不变),但可以用 mypypyright静态类型检查器在编码阶段发现类型错误。配合 typing 模块的 ListDictOptionalUnionCallable 等泛型工具,类型注解让 Python 代码兼具动态灵活性 + 静态安全性。

💡 核心要点:①类型注解是可选语法——变量: 类型def func(arg: 类型) -> 返回类型 ②Python 运行时忽略注解(__annotations__ 存储但不强制),需要 mypy/pyright 做静态检查 ③typing 模块提供泛型:List[int]Dict[str, float]Optional[str](= Union[str, None])④Any 表示任意类型(跳过检查),Callable[[int], str] 表示函数类型 ⑤类型注解提升代码可读性和 IDE 智能提示

教学与演示

一、类型注解基础——给变量和函数"贴上标签"

是什么:类型注解是用冒号语法给 Python 代码标注期望的数据类型。基本语法:变量变量名: 类型 = 值函数参数def func(param: type)返回值-> type。Python 运行时不会强制检查这些注解——代码仍然正常执行。类型注解存储在函数的 __annotations__ 字典中,供静态检查工具使用。

大白话 类型注解就像食品包装上的成分标签——蛋糕包装上写"鸡蛋、面粉、糖"(类型注解:ingredients: List[str]),但你不是非得信这个标签——打开吃发现里面放了辣椒(运行时不检查)。但如果质检员(mypy)先检查一遍,就能在蛋糕上架前发现问题。

为什么:动态类型是 Python 的优势——灵活、代码量少。但项目变大后劣势开始显现:函数参数类型不明确、IDE 无法智能补全、重构时容易遗漏类型不匹配。类型注解解决了这些问题:①IDE 自动提示 ②静态检查在 CI 中自动检测类型错误 ③自文档化——看函数签名就知道传什么、返回什么。

怎么做

import numpy as np
from typing import List, Dict, Tuple, Optional, Union, Any, Callable

# ====== 1. 基础类型注解语法 ======

# 变量类型注解
name: str = "AIQZ"
count: int = 42
scores: List[float] = [0.85, 0.92, 0.78]
config: Dict[str, int] = {"batch_size": 32}

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

print("=== 类型注解基础 ===")
print(greet("Python", 3))
print(f"greet 的注解: {greet.__annotations__}")

# ====== 2. 常用 typing 类型 ======

# Optional[T] = Union[T, None]
def find_user(user_id: int) -> Optional[str]:
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

# Union[A, B] —— 可以是 A 或 B
def process(data: Union[List[float], np.ndarray]) -> np.ndarray:
    if isinstance(data, list):
        return np.array(data)
    return data

# Tuple 指定每个位置的类型
def get_image_info() -> Tuple[int, int, int]:
    return 224, 224, 3

# Callable[[参数类型], 返回类型]
def apply(func: Callable[[float], float], values: List[float]) -> List[float]:
    return [func(v) for v in values]

# Any 表示任意类型——跳过类型检查
def debug_print(obj: Any) -> None:
    print(f"[DEBUG] {type(obj).__name__}: {obj}")

print(f"\nfind_user(1): {find_user(1)}")          # 'Alice'
print(f"find_user(99): {find_user(99)}")          # None
print(f"get_image_info(): {get_image_info()}")

# 使用 Callable
result = apply(lambda x: x ** 2, [1.0, 2.0, 3.0])
print(f"apply(square): {result}")

# ====== 3. AI 场景:带类型注解的模型接口 ======

def normalize_features(
    features: np.ndarray,                         # (N, D) 特征矩阵
    mean: Optional[np.ndarray] = None,            # 可选均值
    std: Optional[np.ndarray] = None              # 可选标准差
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
    """
    标准化特征矩阵
    返回: (标准化后特征, 均值, 标准差)
    """
    if mean is None:
        mean = np.mean(features, axis=0)
    if std is None:
        std = np.std(features, axis=0) + 1e-8
    return (features - mean) / std, mean, std

data = np.random.randn(100, 5)
normalized, mu, sigma = normalize_features(data)
print(f"\n=== 带类型注解的模型接口 ===")
print(f"输入形状: {data.shape}, 输出形状: {normalized.shape}")
print(f"均值: {np.round(mu, 3)}, 标准差: {np.round(sigma, 3)}")

什么用:类型注解在现代 AI 项目中成为事实标准。PyTorch 和 TensorFlow 的代码库大量使用类型注解——函数签名清晰标注 tensor 的形状和类型。IDE 的自动补全依赖类型注解——输入 data. 后自动弹出 .shapemypy 在 CI 中自动检查类型一致性,避免 str 误传为 int 的运行时 bug。

二、typing 模块进阶——泛型、类型别名和 NewType

是什么typing 模块提供了丰富的类型构造工具。泛型List[int])让集合类型更精确;类型别名Vector = List[float])简化复杂类型的书写;NewType 创建名义上不同的类型(用于区分不同含义的同类型值)。Python 3.9+ 中可以用内置泛型 list[int]dict[str, float] 替代 typing.List 等。

大白话 泛型就像"带标签的容器"——以前只说"一个容器"(list),现在说"一个装苹果的容器"(List[Apple]),别人一眼就知道里面装的是什么。类型别名就像给复杂的地址起个简称——不用每次写"XX省XX市XX区XX路",直接说"老王家"。

为什么:在 AI 项目中,类型注解需要描述复杂的数据结构——多层嵌套的张量、函数回调类型、自定义数据类的类型。typing 模块的工具让这些精确描述成为可能,让代码自文档化能力大幅提升。

怎么做

import numpy as np
from typing import List, Dict, Tuple, Optional, Union, Callable
from typing import TypeAlias                         # Python 3.10+

# ====== 1. 类型别名——简化复杂类型 ======

# 类型别名:在 AI 项目中非常实用!
BatchData = Tuple[np.ndarray, np.ndarray]           # (特征, 标签)
Metrics = Dict[str, float]                          # 指标名称 → 值
TransformFn = Callable[[np.ndarray], np.ndarray]    # 变换函数类型

# 使用类型别名
def process_batch(batch: BatchData, transform: TransformFn) -> BatchData:
    features, labels = batch
    transformed = transform(features)
    return transformed, labels

batch: BatchData = (np.random.randn(32, 10), np.random.randint(0, 2, 32))
processed = process_batch(batch, lambda x: x / 255.0)
print("=== 类型别名 ===")
print(f"处理后特征形状: {processed[0].shape}, 标签形状: {processed[1].shape}")

# ====== 2. 复杂泛型——嵌套类型 ======

# 训练配置:多层字典嵌套
TrainingConfig = Dict[
    str,                                           # 模块名称
    Dict[str, Union[int, float, str, bool]]        # 参数名 → 值
]

def parse_config(config: TrainingConfig) -> List[str]:
    """解析训练配置,返回参数摘要"""
    summary = []
    for module, params in config.items():
        for key, value in params.items():
            summary.append(f"{module}.{key} = {value} ({type(value).__name__})")
    return summary

config: TrainingConfig = {
    "optimizer": {"name": "Adam", "lr": 0.001, "weight_decay": 1e-5},
    "training": {"epochs": 100, "batch_size": 32, "use_amp": True},
    "model": {"num_layers": 12, "hidden_dim": 768, "dropout": 0.1},
}

print(f"\n=== 嵌套泛型配置 ===")
for line in parse_config(config):
    print(f"  {line}")

# ====== 3. AI 场景:带完整类型注解的训练函数 ======

# 定义清晰的类型别名
Tensor = np.ndarray
LossFn = Callable[[Tensor, Tensor], float]          # 损失函数类型
OptimizerState = Dict[str, Tensor]                  # 优化器状态

def train_one_epoch(
    model_params: List[Tensor],                     # 模型参数列表
    data_loader: List[Tuple[Tensor, Tensor]],       # 数据加载器
    loss_fn: LossFn,                                # 损失函数
    learning_rate: float = 0.01,                    # 学习率
    grad_clip: Optional[float] = None               # 梯度裁剪阈值
) -> Tuple[List[Tensor], float]:
    """
    训练一个 epoch
    返回: (更新后的参数, 平均损失)
    """
    total_loss = 0.0
    num_batches = 0

    for batch_idx, (features, labels) in enumerate(data_loader):
        # 模拟前向传播
        predictions = features @ model_params[0].reshape(-1, 1)
        loss = loss_fn(predictions.flatten(), labels)

        # 模拟反向传播
        gradients = 2 * (predictions.flatten() - labels) * features.mean(axis=0)

        # 梯度裁剪
        if grad_clip is not None:
            grad_norm = np.linalg.norm(gradients)
            if grad_norm > grad_clip:
                gradients *= grad_clip / grad_norm

        # 参数更新
        model_params[0] = model_params[0] - learning_rate * gradients.reshape(model_params[0].shape)

        total_loss += loss
        num_batches += 1

    avg_loss = total_loss / max(num_batches, 1)
    return model_params, avg_loss

# 创建模拟数据
sample_features = [np.random.randn(10) for _ in range(5)]
sample_labels = [np.random.randn() for _ in range(5)]
loader = [(f, l) for f, l in zip(sample_features, sample_labels)]

mse_loss: LossFn = lambda pred, target: float(np.mean((pred - target) ** 2))
params = [np.random.randn(10, 1)]

print(f"\n=== 训练函数(完整类型注解) ===")
updated_params, loss = train_one_epoch(params, loader, mse_loss, learning_rate=0.1, grad_clip=1.0)
print(f"平均损失: {loss:.6f}")
print(f"参数更新量: {np.mean(np.abs(updated_params[0] - params[0])):.6f}")

什么用:类型别名在 AI 项目中让代码更可读——Batch = Tuple[Tensor, Tensor] 比原始类型名清晰得多。在模型接口中,Callable[[Tensor], Tensor] 明确标注激活函数类型,Dict[str, Tensor] 标注优化器状态。OpenAI 的 triton、HuggingFace 的 transformers 等大型 AI 库都广泛使用类型别名来提高代码可读性。

三、TypedDict 与 Protocol——结构化类型

是什么TypedDicttyping.TypedDict)为字典定义结构——指定哪些键存在、各自的类型是什么。Protocoltyping.Protocol)定义结构化子类型(Structural Subtyping)——不需要显式继承,只要对象实现了 Protocol 要求的方法就符合该类型(鸭子类型的静态版本)。

大白话 TypedDict 就像填写表格——表格规定了每个格子(键)的类型(姓名是 str,年龄是 int),TypedDict 告诉类型检查器这个字典应该有哪些键和对应的类型。Protocol 就像驾照考试——不管你是哪个驾校学的,只要你会开车(实现了相应方法),就符合"司机"这个 Protocol。

为什么:在 AI 中,配置字典、模型输出字典、指标字典随处可见——TypedDict 让这些字典结构文档化,IDE 可以提示键名。Protocol 非常适合 AI 框架设计——定义一个 "Model" Protocol(有 forward 方法),任何实现了 forward 的类都可以作为模型,不需要显式继承基类。

怎么做

import numpy as np
from typing import TypedDict, Protocol, List, Dict, Optional

# ====== 1. TypedDict —— 结构化字典 ======

class TrainingMetrics(TypedDict):
    """训练指标字典——规定结构"""
    epoch: int                                     # 轮次
    train_loss: float                              # 训练损失
    val_loss: float                                # 验证损失
    accuracy: float                                # 准确率
    learning_rate: float                           # 当前学习率

class ModelConfig(TypedDict, total=False):          # total=False → 所有键可选
    """模型配置字典——total=False 表示键可选"""
    num_layers: int
    hidden_dim: int
    dropout: float
    activation: str

def log_metrics(metrics: TrainingMetrics) -> str:
    """记录训练指标——类型检查器确保所有必要键都存在"""
    return (f"Epoch {metrics['epoch']}: "
            f"train_loss={metrics['train_loss']:.4f}, "
            f"val_loss={metrics['val_loss']:.4f}, "
            f"acc={metrics['accuracy']:.2%}")

print("=== TypedDict ===")

# ✅ 正确:所有键都存在且类型匹配
metrics: TrainingMetrics = {
    "epoch": 10,
    "train_loss": 0.234,
    "val_loss": 0.312,
    "accuracy": 0.875,
    "learning_rate": 0.001,
}
print(log_metrics(metrics))

# config 可以只传部分键(total=False)
config: ModelConfig = {"num_layers": 12, "hidden_dim": 768}
print(f"部分配置: {config}")

# ====== 2. Protocol —— 结构化子类型 ======

class Predictable(Protocol):
    """任何有 predict 方法的对象都符合这个协议"""
    def predict(self, x: np.ndarray) -> np.ndarray:
        ...

class LinearModel:
    """线性模型——实现了 predict 方法"""
    def __init__(self, weights: np.ndarray):
        self.weights = weights

    def predict(self, x: np.ndarray) -> np.ndarray:
        return x @ self.weights

class NeuralNetwork:
    """神经网络——也实现了 predict 方法"""
    def __init__(self, layers: List[np.ndarray]):
        self.layers = layers

    def predict(self, x: np.ndarray) -> np.ndarray:
        output = x
        for w in self.layers:
            output = np.tanh(output @ w)
        return output

def evaluate_model(
    model: Predictable,                            # 任何实现了 predict 的对象!
    test_data: np.ndarray,
    test_labels: np.ndarray
) -> float:
    """评估模型——接受任何实现了 predict 的对象"""
    predictions = model.predict(test_data)
    return float(np.mean(predictions == test_labels))

print(f"\n=== Protocol(结构化子类型) ===")

# LinearModel 和 NeuralNetwork 都自动兼容 Predictable——无需显式继承!
linear = LinearModel(weights=np.random.randn(5, 1))
nn = NeuralNetwork(layers=[np.random.randn(5, 8), np.random.randn(8, 1)])

test_x = np.random.randn(10, 5)
test_y = (test_x @ np.ones(5)).reshape(-1, 1) > 0

# 两者都可以传给 evaluate_model——Protocol 让鸭子类型静态化
print(f"LinearModel 预测形状: {linear.predict(test_x).shape}")
print(f"NeuralNetwork 预测形状: {nn.predict(test_x).shape}")

print("Protocol 的核心价值:不需要继承,只要实现了方法就兼容!")

什么用:TypedDict 在 AI 项目中用于规范化配置字典——训练参数、模型超参数、评估指标的字典结构一目了然,IDE 自动补全键名。Protocol 在框架设计中非常重要——PyTorch 的 nn.Module 某种程度上就是 Protocol 思想的体现(任何有 forward 方法的对象都可以作为 Module)。ONNX Runtime 也使用 Protocol 来定义推理接口。

四、mypy 静态类型检查——让注释不只是注释

是什么mypy 是 Python 最流行的静态类型检查器(Static Type Checker)。它读取带类型注解的 Python 代码,在不运行代码的情况下检查类型是否匹配——类似编译型语言的类型检查,但完全可选。使用方法:命令行 mypy script.py,或在 CI 中集成。mypy 不是 Python 运行时的一部分——它独立工作。

大白话 mypy 就像一个"代码校对员"——你写完代码后,校对员拿着一份"类型承诺书"(你的类型注解)逐行检查:你说 namestr,但传了 42——校对员打回去让你修改。校对员不运行代码(不执行程序),只看代码是否符合承诺。这比运行时才发现 TypeError: can only concatenate str (not "int") to str 好多了。

为什么:静态类型检查在大型 AI 项目中价值巨大:(1) 重构安全——改了函数签名后,mypy 自动找到所有不匹配的调用点 (2) CI 自动检查——每次提交自动运行 mypy,类型错误不能合入主分支 (3) 文档保真——类型注解可能过时,但 mypy 强制注解和代码保持一致。

怎么做

import numpy as np
from typing import List, Dict, Optional, Tuple

# ====== 1. mypy 能检测出的常见错误 ======
# 以下代码在 mypy 检查下会报错:

def process_data(data: List[int]) -> float:
    """计算列表的平均值"""
    if not data:
        return 0.0
    return sum(data) / len(data)                  # ✅ 正确

# ❌ mypy 会报错:str 不能传给 List[int]
# result = process_data([1, 2, "hello"])
# error: List item 2 has incompatible type "str"; expected "int"

# ❌ mypy 会报错:返回值类型不匹配
# def wrong_return(x: int) -> str:
#     return x + 1  # error: Incompatible return value type: expected "str", got "int"

# ====== 2. 正确处理 Optional 类型——mypy 的常见严格检查 ======

def safe_divide(a: float, b: Optional[float]) -> Optional[float]:
    """安全除法——b 可能为 None"""
    if b is None or b == 0:
        return None

    # 在 if 块之后,mypy 知道 b 不是 None(类型收窄)
    result: float = a / b                         # ✅ mypy 通过!
    return result

print("=== Optional 类型安全 ===")
print(f"safe_divide(10, 2) = {safe_divide(10.0, 2.0)}")
print(f"safe_divide(10, None) = {safe_divide(10.0, None)}")

# ====== 3. AI 场景最佳实践——完整的类型注解示例 ======

from typing import Sequence

class BatchProcessor:
    """
    批量数据处理器——展示完整的类型注解最佳实践
    使用 TypeVar 实现泛型约束
    """
    batch_size: int                                # 类属性类型注解
    shuffle: bool

    def __init__(self, batch_size: int = 32, shuffle: bool = True) -> None:
        self.batch_size = batch_size
        self.shuffle = shuffle

    def split_into_batches(
        self, data: np.ndarray, labels: np.ndarray
    ) -> List[Tuple[np.ndarray, np.ndarray]]:
        """将数据分割为 batch"""
        num_samples = len(data)
        indices = np.arange(num_samples)

        if self.shuffle:
            np.random.shuffle(indices)

        batches = []
        for start in range(0, num_samples, self.batch_size):
            end = min(start + self.batch_size, num_samples)
            batch_indices = indices[start:end]
            batches.append((data[batch_indices], labels[batch_indices]))

        return batches

# 使用示例
processor = BatchProcessor(batch_size=4, shuffle=False)
data = np.arange(10).reshape(-1, 1).astype(float)
labels = np.array([0, 1, 0, 1, 0, 0, 1, 1, 0, 1])
batches = processor.split_into_batches(data, labels)

print(f"\n=== 批次分割器 ===")
print(f"总样本: {len(data)}, batch_size={processor.batch_size}")
for i, (batch_data, batch_labels) in enumerate(batches):
    print(f"  Batch {i}: data shape={batch_data.shape}, labels={batch_labels}")

# ====== 4. 类型注解的价值总结 ======

print(f"\n=== 类型注解价值总结 ===")
print("1. IDE 智能补全 — 输入 'data.' 自动提示 .shape, .mean(), .reshape() 等方法")
print("2. 静态错误检测 — mypy 在生产前发现 type error,不是运行时才发现")
print("3. 自文档化代码 — 函数签名就是文档,不需要额外注释说明参数类型")
print("4. 重构安全性 — 修改函数签名后,mypy 自动找到所有不兼容的调用点")
print("5. 团队协作 — 类型注解是代码的 API 契约,新成员快速理解接口")

什么用:在 AI 生产项目中,mypy 是 CI 流水线的标配。Google 的 TensorFlow、Meta 的 PyTorch、Microsoft 的 ONNX Runtime 等大型项目都有严格的 mypy 检查。类型注解 + mypy 让 Python 代码获得了接近编译型语言的类型安全性,同时保留了动态语言的灵活性——"渐进式类型"(Gradual Typing)让团队可以逐步添加类型注解,不影响现有代码。

概念关系图谱

概念核心含义与AI的关系关联概念
类型注解变量: 类型 标注期望类型函数签名标注 tensor 形状类型__annotations__、mypy
OptionalOptional[T] = T 或 None可选参数、可能缺失的返回值Union、None
UnionUnion[A, B] = A 或 B接受多种输入类型Optional、多态
CallableCallable[[Args], Return]回调函数、损失函数、激活函数高阶函数、lambda
TypedDict结构化字典定义训练配置、指标字典的文档化Dict、类型安全
Protocol鸭子类型的静态版本模型接口(有 forward 即可)ABC、鸭子类型
mypyPython 静态类型检查器CI 流水线中的类型检查pyright、类型安全
渐进式类型逐步添加类型注解的策略老项目逐步类型化类型注解、mypy

重点答疑

Q1: 类型注解会影响 Python 运行时的性能吗?

基本不会。 Python 运行时仅在函数定义时将注解字符串化后存入 __annotations__,不进行任何类型检查。唯一的开销是函数定义时对注解表达式的求值(Python 3.10 之前)——这个开销极小。from __future__ import annotations(PEP 563)可以让注解完全变成惰性求值的字符串,零运行时开销。类型检查完全由 mypy/pyright 在编码阶段完成,不影响运行时性能。

Q2: typing.Listlist 有什么区别?Python 3.9+ 应该用哪个?

Python 3.9 之前,list[int] 不行(内置 list 不支持 [] 泛型语法),只能用 typing.List[int]。Python 3.9+(PEP 585)中,内置类型直接支持 list[int]dict[str, float] 等泛型写法。现代代码优先使用内置泛型(更简洁),from __future__ import annotations 可以让你在 Python 3.7+ 也使用内置泛型。

Q3: 什么时候该用 Any,什么时候不该用?

该用:(1) 和老旧库交互、类型确实不确定 (2) 快速原型阶段——先标注 Any,后续收紧 (3) **kwargs: Any 这种确实接受任意参数的地方。不该用:(1) 只因为"懒得写类型"——这会失去类型检查的价值 (2) 公共 API 中——对外接口应该精确。原则:Any 就像"紧急出口"——有正当理由时用,但不要因为"方便"而滥用。

Q4: mypy 报错了但代码运行正常——怎么办?

有三种处理方式:(1) 修复代码——如果 mypy 的警告是合理的,修改代码以匹配类型 (2) 修复注解——如果注解写错了,更新注解 (3) 抑制警告——如果 mypy 误报或特殊情况,用 # type: ignore[error-code] 注释来抑制特定行的特定错误。原则是尽量让代码和注解一致,而不是大量使用 # type: ignore

章节单词汇总

英文音标术语/释义
annotation/ˌænəˈteɪʃən/注解;类型提示的正式名称
optional/ˈɑːpʃənəl/可选的;Optional[T] 表示 T 或 None
union/ˈjuːnjən/联合;Union[A,B] 表示 A 或 B 类型
protocol/ˈproʊtəkɔːl/协议;结构化子类型,鸭子类型的静态版
generic/dʒəˈnerɪk/泛型;参数化的类型如 List[int]
static typing/ˈstætɪk ˈtaɪpɪŋ/静态类型;编译/检查时确定类型
gradual typing/ˈɡrædʒuəl ˈtaɪpɪŋ/渐进式类型;逐步添加类型注解
type checker/taɪp ˈtʃekər/类型检查器;如 mypy、pyright

面试练习

Q1 [单选] 以下哪个是正确的函数类型注解写法?

  • A. def f(x: str) -> None
  • B. def f(x: str) return None
  • C. def f(x: str) -> None:
  • D. def f(x -> str): None
解答:C 正确。语法:def func(param: type) -> return_type:。A 少冒号,B 用了 return 不是 ->,D 混淆了参数注解和返回注解。

Q2 [单选] Python 运行时如何处理类型注解?

  • A. 严格检查并抛出 TypeError
  • B. 存储到 __annotations__ 但不强制检查
  • C. 完全忽略,不会存储
  • D. 将数据自动转换为注解的类型
解答:B 正确。Python 运行时不会强制检查类型注解——它只是把注解信息存入 __annotations__ 字典。类型检查由外部工具(mypy)完成。

Q3 [单选] Optional[str] 等价于以下哪个?

  • A. Union[str, str]
  • B. Union[str, None]
  • C. Union[str, Any]
  • D. Union[str, Optional]
解答:B 正确。Optional[T] 等价于 Union[T, None]——表示可以是 T 类型或 None

Q4 [多选] 以下哪些是 typing 模块提供的类型?

  • A. List[int]
  • B. Dict[str, float]
  • C. Callable[[int], str]
  • D. array[int]
解答:A、B、C 正确。D 错误——array 不是 typing 模块的类型,Python 内置有 array.array 但不是泛型。

Q5 [单选] mypy 属于什么工具?

  • A. Python 包管理器
  • B. 代码格式化工具
  • C. 静态类型检查器
  • D. 测试框架
解答:C 正确。mypy 是 Python 最流行的静态类型检查器,在不运行代码的情况下检查类型注解的一致性。

Q6 [多选] 类型注解的好处包括?

  • A. IDE 智能代码补全
  • B. 静态发现类型错误(通过 mypy)
  • C. 自文档化——函数签名即文档
  • D. 运行时自动类型转换
解答:A、B、C 正确。D 错误——Python 运行时不会自动进行类型转换,类型注解不影响运行时行为。

Q7 [单选] from typing import Any 中的 Any 表示什么?

  • A. 任意类型,跳过类型检查
  • B. 只能是对象类型
  • C. 只能是 None 类型
  • D. 只能是基本类型(int/str/float 等)
解答:A 正确。Any 表示"任意类型"——与该值相关的所有类型检查都会被跳过。它是类型系统的逃生舱。

Q8 [单选] Callable[[int, float], str] 描述的是什么?

  • A. 一个接收 str、返回 int 和 float 的函数
  • B. 一个接收 int 和 float、返回 str 的函数
  • C. 一个列表,元素为 int 和 float
  • D. 一个字典,键为 int,值为 float
解答:B 正确。Callable[[参数类型列表], 返回类型]——[int, float] 是参数类型列表,str 是返回类型。

Q9 [多选] 关于 Protocol 的说法正确的是?

  • A. Protocol 是结构化子类型——不需要显式继承
  • B. 只要对象实现了 Protocol 要求的方法,就兼容该类型
  • C. Protocol 要求所有类必须显式继承它
  • D. Protocol 是鸭子类型的静态类型版本
解答:A、B、D 正确。C 错误——这正是 Protocol 的核心优势:不需要显式继承,实现了方法就自动兼容(结构化子类型)。

Q10 [单选] Python 3.9+ 中,list[int]typing.List[int] 的关系是?

  • A. 完全不同,语义不同
  • B. 等价——Python 3.9+ 内置类型支持泛型语法
  • C. list[int] 只能在函数签名中使用
  • D. list[int] 会运行时检查类型
解答:B 正确。Python 3.9(PEP 585)开始,内置类型直接支持泛型语法——list[int] 等价于 typing.List[int]。推荐使用内置泛型(更简洁)。