如何用mypy为无法导入的外部库创建伪类型实现类型检查?
外部不可用SDK的类型检查解决方案
针对你遇到的嵌入式SDK无法在开发环境正常运行、无类型注解,但需要为调用代码添加类型检查的问题,以下是几种可行方案:
1. 使用类型存根文件(.pyi)
创建与SDK目录结构完全匹配的类型存根文件(后缀为.pyi),仅定义你需要调用的类、方法的类型签名,无需实现具体逻辑。
比如SDK模块路径为embedded_sdk.device,则创建embedded_sdk/device.pyi文件:
# embedded_sdk/device.pyi class Sensor: def __init__(self, pin: int, baud_rate: int = 9600) -> None: ... def read_data(self) -> float: ...
- 类型检查工具(如mypy)会优先读取存根文件进行类型校验,运行时不会加载存根
- 当SDK可用时,若真实代码有类型注解会覆盖存根,无注解则存根依然生效
2. 利用typing.TYPE_CHECKING做条件导入
TYPE_CHECKING是一个仅在类型检查阶段为True的常量,可借此实现"检查时用真实/伪类型,运行时跳过导入"的逻辑:
from typing import TYPE_CHECKING, Any if TYPE_CHECKING: # 类型检查阶段尝试导入真实类型,失败则定义伪类型 try: from embedded_sdk.device import Sensor except ImportError: class Sensor: def __init__(self, pin: int, baud_rate: int = 9600) -> None: ... def read_data(self) -> float: ... else: # 运行时用Any避免导入报错 Sensor = Any
- 本地开发时,类型检查工具会基于定义的伪类型校验参数传递
- SDK可用时,自动使用真实类型进行更精准的检查
3. 用Protocol定义接口(推荐)
如果只关注接口兼容性而非具体类型,可通过Protocol定义你需要的方法签名,让代码遵循该接口:
from typing import Protocol, TYPE_CHECKING, cast # 定义SDK类需要遵循的接口 class SensorProtocol(Protocol): def __init__(self, pin: int, baud_rate: int = 9600) -> None: ... def read_data(self) -> float: ... # 类型检查阶段用Protocol,运行时动态获取真实类型或占位 if TYPE_CHECKING: SensorType = SensorProtocol else: try: from embedded_sdk.device import Sensor as SensorType except ImportError: SensorType = object # 调用代码标注为Protocol类型,确保参数和返回值符合要求 def process_sensor_data(sensor: SensorType) -> float: raw_data = sensor.read_data() return raw_data * 1.2 # 实例化逻辑:SDK可用则用真实类,否则用本地模拟类 try: sensor = SensorType(pin=3) except: class MockSensor: def __init__(self, pin: int, baud_rate: int = 9600) -> None: self.pin = pin def read_data(self) -> float: return 22.5 # 用cast告诉类型检查工具模拟类符合Protocol sensor = cast(SensorProtocol, MockSensor(pin=3))
- 无论SDK是否可用,类型检查都能验证代码是否符合接口定义
- 模拟类只需实现接口中的方法,无需复刻SDK全部功能
注意事项
- 伪类型、存根或Protocol只需定义你实际用到的方法和属性,无需完整复刻SDK
- 若SDK可用时需要精准校验,确保存根/Protocol的定义与真实SDK接口一致
- 运行时需做好异常捕获,避免因SDK不可用导致程序崩溃
内容的提问来源于stack exchange,提问作者Jan Spurny
相关产品推荐
相关产品推荐

