Python跨目录循环导入触发AttributeError报错的原因及解决方案咨询
报错触发原因
这是Python循环导入场景下的父模块属性绑定时机问题,核心逻辑如下:
- Python导入模块时,会先在
sys.modules注册模块占位对象,再逐行执行模块内代码。子模块只有完全导入完成后,才会被绑定为父模块的可访问属性。 - 你运行
main.py导入dir1.sub1时的执行流程为:- 先注册
dir1、dir1.sub1到sys.modules,开始执行sub1.py代码 - 执行到
import dir1.sub2时,注册dir1.sub2到sys.modules,开始执行sub2.py代码 sub2.py导入dir1.sub1时发现该模块已经注册,直接返回继续执行,完成f2函数定义sub2.py执行完毕,sub2被绑定为dir1的属性,回到sub1.py执行dir1.sub2.f2()- 此时
sub1.py还没完全执行完,sub1还没有被绑定到dir1的属性上,所以调用dir1.sub1.f1()时就会抛出属性不存在的错误。
- 先注册
- 改为导入
dir1.sub2时报错逻辑完全一致,只是反过来变成sub2还没完成绑定就被访问。 - 两个文件移到根目录后可正常运行的原因是:此时导入的是根级模块,不需要通过父模块
dir1访问属性,只要模块在sys.modules中存在就能直接拿到已定义的函数,不会触发属性检查。
避免方案
你可以根据场景选择以下任意一种方案:
- 调整顶层执行逻辑:不要在模块顶层直接执行跨模块调用,把调用逻辑放到函数内部,等所有模块导入完成后再主动触发。比如修改
sub1.py:def f1() -> None: print("f1") import dir1.sub2 def run(): dir1.sub2.f2()main.py改为:import dir1.sub1 dir1.sub1.run() - 改用相对导入:在
dir1目录下添加空的__init__.py文件标记为Python包,修改两个子文件的导入方式,直接访问模块对象不需要经过父模块:sub1.py修改为:def f1() -> None: print("f1") from . import sub2 sub2.f2()sub2.py修改为:from . import sub1 def f2() -> None: sub1.f1() print("f2") - 延迟导入:把循环依赖的导入放到函数内部,调用时模块已经完成初始化绑定:
# sub2.py 调整为 def f2() -> None: import dir1.sub1 dir1.sub1.f1() print("f2")
内容的提问来源于stack exchange,提问作者DigitalDetective47
相关产品推荐
相关产品推荐

