Python包中互相引用的自定义类型如何解决循环导入并简化调用?
方案1:顶层包挂载类 + 包级导入引用(无性能损耗,完美适配短前缀调用)
该方案可以直接实现类似np.int8的短前缀调用效果,同时彻底解决循环导入问题,无额外运行时性能损耗。
首先整理你的包结构,假设你的包名为mypkg,文件结构如下:
mypkg/ ├── __init__.py ├── custom_type1.py ├── custom_type2.py └── base_type.py
第一步:配置顶层__init__.py
将两个类挂载到包顶层属性,后续用户可以直接通过import mypkg as mp的方式,用mp.CustomType1、mp.CustomType2调用对应类:
# mypkg/__init__.py # 先导入两个类型模块,仅导入模块不会触发循环依赖 from . import custom_type1, custom_type2 # 将类绑定到包顶层属性 CustomType1 = custom_type1.CustomType1 CustomType2 = custom_type2.CustomType2 # 配置__all__支持from mypkg import *的导入方式 __all__ = ["CustomType1", "CustomType2"]
第二步:修改类文件的引用逻辑
不要在类文件顶层直接导入另一个类,改为导入顶层包,引用包下的类即可:
# mypkg/custom_type1.py from .base_type import BaseType import mypkg # 直接导入顶层包 class CustomType1(BaseType): def __init__(self, data): if isinstance(data, mypkg.CustomType2): # 可直接调用对方的公开方法,无额外开销 pass
# mypkg/custom_type2.py from .base_type import BaseType import mypkg class CustomType2(BaseType): def __init__(self, data): if isinstance(data, mypkg.CustomType1): pass
注:类定义阶段不会执行__init__内部的代码,所以类定义时包属性未绑定不会有任何问题,等到实际实例化类的时候,包初始化已经完成,属性已经全部挂载完毕
方案2:模块级延迟导入(适合大型项目,降低首次导入开销)
如果你的包包含大量类型,首次全量导入模块开销大,可以用Python3.7+支持的模块级__getattr__实现按需加载,同时保留短前缀调用的用法:
# mypkg/__init__.py __all__ = ["CustomType1", "CustomType2"] def __getattr__(name): if name == "CustomType1": from .custom_type1 import CustomType1 return CustomType1 elif name == "CustomType2": from .custom_type2 import CustomType2 return CustomType2 raise AttributeError(f"module {__name__} has no attribute {name}")
两个类文件的引用逻辑和方案1完全一致,第一次访问类的时候才会加载对应的模块,既没有循环导入问题,也不会有局部导入的重复开销。
注意事项
- 不要在两个类文件的顶层直接写
from .xxx import CustomTypeX,这种写法会在模块导入阶段就要求对方模块已经完成类定义,必然触发循环依赖 - 如果需要使用类型注解,可以加
from __future__ import annotations,所有注解直接写mypkg.CustomTypeX即可,不需要额外导入
内容的提问来源于stack exchange,提问作者orangeman
相关产品推荐
相关产品推荐

