问题:如何避免在写代码的时候,出现循环引入的问题!
- 打包镜像完成之后,启动服务的时候,发现容器里面的服务无法正常启动。随即,手动执行了下,发现如下报错信息:
# 运行程序
root@42ebf5we93bd: /opt/app# python3 -m run_web
Traceback (most recent call last):
File "/usr/bin/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/bin/python3.6/runpy.py", line 193, in _run_code
exec(code, run_globals)
File "/opt/app/config.py", line 8, in <module>
from app.common.crypto_xxx import xxx_encrypt
File "/opt/app/common/crypto_xxx.py", line 6, in <module>
from app import config
File "/opt/app/config.py", line 8, in <module>
from app.common.crypto_util import xxx_encrypt
ImportError: cannot import name 'xxx_encrypt'
- 通过查看
Python
的堆栈信息,可以判断出,是因为循环引入导致的。A
模块 ->B
模块B
模块 ->A
模块
# module_a
from module_b import fun_b
def fun_a():
print('-> fun_a')
fun_b()
def fun_c():
print('-> fun_c')
if __name__ == '__main__':
fun_a()
# module_b
from module_a import fun_c
def fun_b():
print('-> fun_b')
fun_c()
- 然后,你在运行的时候,就会出现以下的错误信息。这就和我们上述遇到的问题是一样的,这就是循环引用导致的,上述代码可以帮助我们很好的理解这个问题。
# 循环引用
$ python3 module_a.py
Traceback (most recent call last):
File "module_a.py", line 1, in <module>
from module_b import fun_b
File "/Users/escape/Fuckcode/escape/Scripts/module_b.py", line 1, in <module>
from module_a import fun_c
File "/Users/escape/Fuckcode/escape/Scripts/module_a.py", line 1, in <module>
from module_b import fun_b
ImportError: cannot import name 'fun_b' from partially initialized module 'module_b'
解决:如何避免在写代码的时候,出现循环引入的问题!
导入的实质 导入其实是要将被导入模块的所有的顶格代码都执行一遍,遇到函数和类的定义会作申明。
- 循环引用
- 首先导入
B
模块中的fun_b
方法 - 发现
B
模块,又导入了A
模块的fun_c
方法 - 又回到
A
模块,又导入B
模块的fun_b
方法
[解决方法一] 直接导入模块名 - 通过模块调用其中的函数
.py
.pyc
# module_a
import module_b
def fun_a():
print('-> fun_a')
module_b.fun_b()
def fun_c():
print('-> fun_c')
if __name__ == '__main__':
fun_a()
- [解决方法二] 使用延迟导入 -
lazy import
.py
.pyc
.so
# module_a
def fun_a():
print('-> fun_a')
from module_b import fun_b
fun_b()
def fun_c():
print('-> fun_c')
if __name__ == '__main__':
fun_a()
- [解决方法三] 重新设计代码结构 - 将代码和并或者分离
.py
.pyc
.so
首先,出现这种问题是因为没有规划好层级,哪些模块和哪些模块逻辑上应该在一起,哪些模块是公共的依赖项,哪些是实际的业务代码。其次,就是需要注意对应的 Python
代码是否需要编写成为加密的 pyc
或者 so
文件,如果需要的话,就需要选择合适的处理方法,进行解决。最好,循环引用是小问题,代码以后难维护、理不清实现的逻辑才是大问题。治本的还是要重新划分模块,逻辑理顺了就不会出现循环 import
的问题。