Python循环引用问题处理


问题:如何避免在写代码的时候,出现循环引入的问题!

  • 打包镜像完成之后,启动服务的时候,发现容器里面的服务无法正常启动。随即,手动执行了下,发现如下报错信息:
# 运行程序
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 的问题。


文章作者: Escape
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Escape !