Python 3.10+兼容静态分析的类型容器/注册表类方案问询
问题:Python 3.10+ 类型容器的静态类型推断方案
背景约束
- 内部包必须遵循
from <package> import X的导入规范 - 目标模块包含100+自定义类型
- 消费模块需要为目标模块的每种类型生成1:1对应逻辑,当前需导入大量类型,操作繁琐
现有两种方案的缺陷:
- 导入整个包作为命名空间容器:违反项目导入规范
- 用
TypeAlias创建含100+属性的不可实例化类:代码重复冗余
解决方案
一、Python原生方案(推荐)
核心思路是结合静态类型检查分支(TYPE_CHECKING)和动态属性获取(__getattr__),实现一个类型注册表类,既遵守导入规范,又让mypy等工具能正确推断类型,同时避免重复代码。
步骤1:目标类型模块(types_module.py)
存放所有自定义类型:
from typing import TypedDict class User(TypedDict): id: int name: str class Product(TypedDict): sku: str price: float # 其他100+类型...
步骤2:类型注册表类(type_registry.py)
利用TYPE_CHECKING为静态分析工具提供明确的类型注解,运行时通过__getattr__动态获取类型,同时可以用脚本自动生成静态注解块(避免手动写100+属性):
from typing import Any, TYPE_CHECKING # 静态分析分支:给mypy提供明确的类型属性 if TYPE_CHECKING: from .types_module import ( User, Product, # 此处可通过脚本自动生成所有类型的导入和赋值 ) class TypeRegistry: User = User Product = Product # 对应所有类型的属性声明 # 运行时分支:动态获取类型,避免代码冗余 else: class TypeRegistry: @classmethod def __getattr__(cls, name: str) -> Any: from .types_module import __dict__ as types_dict if name not in types_dict: raise AttributeError(f"TypeRegistry has no attribute '{name}'") return types_dict[name]
步骤3:消费模块使用
只需导入注册表类即可,mypy能正确推断类型:
from .type_registry import TypeRegistry def process_user(user: TypeRegistry.User) -> None: print(user["name"]) def process_product(product: TypeRegistry.Product) -> None: print(product["sku"])
脚本生成静态注解块:针对100+类型,可编写简单Python脚本遍历types_module.py的成员,自动生成TYPE_CHECKING分支内的导入和属性声明,避免手动维护。
二、Pydantic 2方案
如果你的类型都是Pydantic模型,可直接利用Pydantic 2的模型注册表和类型增强能力,核心思路和原生方案一致,仅需调整注册表实现以适配Pydantic:
from typing import Any, TYPE_CHECKING from pydantic import BaseModel if TYPE_CHECKING: from .types_module import ( UserModel, ProductModel, # 所有Pydantic模型类型 ) class PydanticTypeRegistry: UserModel = UserModel ProductModel = ProductModel else: from pydantic._internal._model_registry import ModelRegistry class PydanticTypeRegistry: _registry = ModelRegistry() @classmethod def __getattr__(cls, name: str) -> type[BaseModel]: model = cls._registry.get(name) if not model: from .types_module import __dict__ as types_dict model = types_dict.get(name) if not model: raise AttributeError(f"PydanticTypeRegistry has no attribute '{name}'") return model
消费时用法和原生方案完全一致,Pydantic会自动处理模型的类型校验和推断。
关键说明
- 两种方案均符合
from <package> import X的导入规范,消费模块仅需导入注册表类 - 静态分析工具(如mypy)通过
TYPE_CHECKING分支的明确注解实现类型推断,运行时通过动态获取避免代码冗余 - 针对100+类型的场景,脚本生成静态注解块是核心,可大幅降低维护成本
内容的提问来源于stack exchange,提问作者pstatix
相关产品推荐
相关产品推荐

