Python中'import pkg.a as a'与'from pkg import a'的区别及循环导入报错排查
问题1:
import pkg.a as a 和 from pkg import a 的区别 这俩导入方式乍一看功能类似,但实际在命名空间绑定、依赖逻辑上有不少细节差异,我给你拆解下:
绑定的对象不一样
import pkg.a as a:这里的a直接绑定的是pkg.a这个模块对象。之后你要调用模块里的函数/类,得用a.xxx()这种形式。from pkg import a:这里的a绑定的是pkg包中名叫a的具体对象——它可能是子模块,也可能是pkg/__init__.py里导出的函数、类甚至变量。比如如果__init__.py里写了from .a import my_func as a,那你导入的a就是那个函数,不是模块。
对包导出的依赖不同
- 用
import pkg.a as a时,Python会直接去找pkg下的a.py或者a子包,不需要__init__.py做任何额外的导出配置。 - 用
from pkg import a时,如果a是子模块,在旧版本Python里需要__init__.py显式导出(比如加__all__ = ['a']),不过Python 3.3+的隐式命名空间包可以不用,但如果a是__init__.py里的对象,那必须是在这个文件里定义或导入的。
- 用
重新导入的行为有差异
- 再执行
import pkg.a as a时,Python会先查sys.modules里有没有pkg.a,有就直接复用,不会重新加载模块。 - 而
from pkg import a如果是导入模块,逻辑和上面一样;但如果是导入__init__.py里的对象,要是后续__init__.py里的a指向变了,你得用importlib.reload才能刷新这个引用。
- 再执行
问题2:循环导入导致的
AttributeError 解决办法 这个错误是循环导入里非常典型的情况,我先给你捋清楚为什么会报错,再给几个可行的解决办法:
报错原因
当你执行import test.a时,Python的模块加载流程是这样的:
- 先创建
test包的模块对象,把它加入sys.modules(这时候test是空的,还没任何子模块属性)。 - 开始加载
test/a.py,第一行就执行import test.b。 - 接着加载
test/b.py,第一行是import test.a。这时候sys.modules里已经有test了,但test.a还没加载完(步骤2还在跑),所以test对象上根本没有a这个属性,于是就抛出了AttributeError。 - 等
test/b.py加载完回到test/a.py继续执行时,test才会被添加上a属性,但这时候错误已经触发了。
解决办法
这里有几个常用的方案,你可以根据自己的代码场景选:
延迟导入(把导入放到函数内部)
把导入语句移到函数里面,这样只有当函数被调用时才会执行导入,这时候两个模块都已经加载完成了:a.py修改后:def a(): import test.b print("a")b.py修改后:def b(): import test.a print("b")直接导入模块,跳过包前缀
把a.py里的import test.b改成import b,b.py里的import test.a改成import a。不过要注意,得确保test包所在的目录在Python的sys.path里,或者你是在包的父目录下运行代码。重构代码,消除循环依赖
最彻底的办法是把两个模块互相依赖的部分抽出来,放到一个新的共享模块里,比如test/common.py,然后a.py和b.py都只导入common里的内容,这样就打破了循环。
比如重构后的目录结构:
/test __init__.py a.py b.py common.py
把原来两个模块都要用的逻辑放到common.py里,a.py和b.py就不需要互相导入了。
内容的提问来源于stack exchange,提问作者wei
相关产品推荐
相关产品推荐

