循环导入的最好的解决方法是从架构上优化,即调整模块和模块成员变量的设计。一个好的原则是:可导出的成员变量,都不该该依赖于导入进来的成员变量。
可是在业务开发的过程当中,总会遇到经过架构层面解决不了的导入问题,这时候就只能经过语言层面来解决了。html
目录结构(下面的案例的目录结构都是这样的):python
root.py /pack1 __init__.py module_a.py /pack2 __init__.py module_b.py module_c.py module_d.py
首先看一下什么是循环导入和循环导入的缘由。
root.py架构
from pack1.module_a import class_a
module_a.py优化
print "start init module a" from pack2.module_b import class_b class class_a(): def f(self): class_b print "init module a"
module_b.py设计
print "start init module b" from pack1.module_a import class_a class class_b(): def f(self): class_a print "init module b"
会报错:code
start init module a start init module b Traceback (most recent call last): File "E:/my_demo/demo2016/bѭ������/s2/root.py", line 2, in <module> from pack1.module_a import class_a File "E:\my_demo\demo2016\bѭ������\s2\pack1\module_a.py", line 2, in <module> from pack2.module_b import class_b File "E:\my_demo\demo2016\bѭ������\s2\pack2\module_b.py", line 2, in <module> from pack1.module_a import class_a ImportError: cannot import name class_a
代码执行的流程:htm
from pack1.module_a import class_a
,发现须要导入模块module_afrom pack2.module_b import class_b
时,发现须要导入模块module_bfrom pack1.module_a import class_a
时,发现须要导入模块module_a,可是此时已经有module_a的globals了,因此直接访问字典里的class_a,可是因为module_a的globals仍是空的,即里面没有class_a,因此抛出异常参考文档
因此根本缘由是:在导入的时候,module_b须要访问module_a的变量class_a,可是class_a没有初始化完成
因此解决方法有两个:对象
import ...
代替 from...import...
root.py开发
import pack1.module_a
module_a.py文档
print "start init module a" import pack2.module_b class class_a(): def f(self): m_b.class_b print "init module a" if __name__ == '__main__': pass
module_b.py
print "start init module b" import pack1.module_a class class_b(): def f(self): pack1.module_a.class_a print "init module b"
module_a和module_b都会被编译,终端会输出:
start init module a start init module b init module b init module a
即首先编译a,编译过程当中发现须要编译b,编译b完成后,编译a剩下的部分、
这个案例不使用from....import....
,而使用import
,这样是能够成功循环导入的,不过一个缺点是,每次访问module的时候,都须要写全路径,例如pack1.module_a.class_a
,很是繁琐。
一个优化的方案是导入的时候,使用import....as...
例如:import pack1.module_a as m_a
。可是很奇怪的是,在module_a中能够这样用,可是在module_b中不能够,不然就会致使报错。还有若是把roo.py改成import pack2.module_b
,就会反过来,即module_b中能够这样用,可是在module_a中不能够。因此准确点应该是在root.py导入的模块中可使用,可是在其余模块不能使用。因此import....as...
这个方案并很差。
注意,import...
只能import到模块,不能import模块里面的成员变量,例如import pack1.module_a.class_a
是不能够的
这个方案的缺点就是访问模块里面的成员变量太繁琐
root.py
from pack1.module_a import class_a
module_a.py
print "start init module a" #from pack2.module_b import class_b #放在这里会报错 class class_a(): def f(self): # m_b.class_b pass from pack2.module_b import class_b #放在这里不会 class class_c(): def f(self): class_b print "init module a"
module_b.py
print "start init module b" from pack1.module_a import class_a class class_b(): def f(self): class_a print "init module b"
当存在相似的依赖关系:class_c依赖class_b依赖class_a
,而后class_a和class_c在同一个模块时,可使用这种方案。
把from pack2.module_b import class_b
这句放在class_a后面,这样在module_b中访问module_a.class_a是成功的,由于class_a的定义代码已经执行完成,并被添加到module_a的globals中。
root.py
from pack1.module_a import func_a print 'root start run func a' func_a() print 'root end run func a'
module_a.py
print "start init module a" def func_a(): from pack2.module_b import func_b func_b() print 'run func a' print "init module a"
module_b.py
print "start init module b" def func_b(): from pack1.module_a import func_a print 'run func b' print "init module b"
输出:
start init module a init module a root start run func a start init module b init module b run func b run func a root end run func a
在须要使用func_b的时候,才进行导入操做,这样在执行module_b的时候,module_a已经初始化完成,module_a的globals已经有func_a了,因此导入不会报错。
import sys from pack1.module_a import func_a print sys.modules # {'pack1': <module 'pack1' from 'E:\my_demo\demo2016\bѭ������\s4\pack1\__init__.pyc'>,} print sys.modules['pack1.module_a'].__dict__ # {'func_a': <function func_a at 0x0254FB30>, '__doc__': None} sys.modules['pack1.module_a'].func_a_tmp=sys.modules['pack1.module_a'].func_a
经过sys.modules能够访问全部当前已导入的模块。
modules是一个字典,key是模块的路径,例如pack1.module_a
,value是一个模块对象
模块对象中,属性名是模块中全局变量的名字,即sys.modules['pack1.module_a'].__dict__
等于module_a里面的globals()
因此,当在module_b中执行from pack1.module_a import class_a
时,至关于执行代码:
import sys if 'pack1.module_a' in sys.modules: if hasattr(sys.modules['pack1.module_a'],"class_a"): sys.modules['pack2.module_b'].class_a=sys.modules['pack1.module_a'].class_a else: raise Exception(u"循环导入异常") else: #执行导入pack1.module_a的操做,也就是初始化一个module对象,而后令sys.modules['pack1.module_a']=这个对象
因此解决循环导入的问题,就至关于使上面的代码不会执行到raise Exception(u"循环导入异常")
这一句,方案一和方案二都是经过这种方法解决的。
未经容许,请不要转载