模块循环引用是大型开发中常遇到的问题,高级编程语言发展到今天仍有很多语言存在这类问题,目前也有一些年轻的语言考虑过这类问题(如在go的工程中,代码间存在模块循环引用会导致编译出错)。
很遗憾今天的主角:python工程中依然存在这类问题。接下来我将结合实例,详细解析模块循环引用出现的原因以及如何避免循环引用。
首先介绍示例工程结构:
– src
|-- loop1
| |-- a.py
| |-- b.py
|-- loop2
| |-- c.py
|-- mytest.py
模块之间的引用关系:mytest引用loop1包中的a模块,a模块引用loop2包中的c模块,c模块引用loop1中的b模块,b模块引用同级中的a模块
模块间的引用关系见下图:
模块内容:
# .mytest.py
from loop1.a import func_a
func_a()
#--------------------------
# ./loop1/a.py
from loop2.c import func_c
def func_a():
print("use func a")
func_c()
#--------------------------
# ./loop2/c.py
from loop1.b import func_b
def func_c():
print("use func c")
func_b()
#--------------------------
# ./loop1/b.py
from loop1.a import func_a
def func_b():
print("use func b")
func_a()
报错显示:
Traceback (most recent call last):
File "mytest.py", line 1, in <module>
from loop1.a import func_a
File "/Users/lwb/PycharmProjects/llearn_python/loop1/a.py", line 1, in <module>
from loop2.c import func_c
File "/Users/lwb/PycharmProjects/llearn_python/loop2/c.py", line 1, in <module>
from loop1.b import func_b
File "/Users/lwb/PycharmProjects/llearn_python/loop1/b.py", line 1, in <module>
from loop1.a import func_a
ImportError: cannot import name 'func_a'
分析出错的原因前,我们需要搞清楚import语句的执行流程。在引用模块时,需要将模块所有的顶格代码都执行一遍,遇到函数和类的定义会作声明。
这一套引用流程本没有问题,但是在按行执行顶格代码时,如果碰到引用别的模块则会转到别的模块的定格代码,最终实现所有模块定格代码都执行为止。
如果出现模块的循环引用,这一美好的流程将会被打破。如样例工程,在mytest.py定格代码中先引用将执行从a模块中引用fu nc_a方法、a模块引用c模块func_c、c模块引用b模块func_b方法、最终b模块引用了a模块func_a方法;因为a模块定格代码未执行到func_a方法,所以会报不能引入func_a的错误。
了解模块的引入和执行机制后,依据需求目前有3种解决方案:
# ./loop1/a.py
def func_a():
print("use func a")
from loop2.c import func_c
func_c()
# ./loop1/a.py
import loop2.c
def func_a():
print("use func a")
loop2.c.func_c()