在开发稍大一些的 python 程序中,稍不小心就会发生 import 错误,导致这种错误的原因可能有很多,但是,今天我遇到的是因为循环导入而导致的问题,代码可能类似于这样:

//model.py
from services import XXXService

class ModelXXX():
    ....

    def save():
        XXXService.xxxx()

//services.py
from model import ModelXXX

class XXXService():
    ...
    def yyy():
        ModelXXX()


对,就这么坑爹得出现了 import 的错误,然后我就查找问题了,很明显,又是前人的锅,前人居然在 Model 里面写了一大串的逻辑代码,然后导致了编写 Service 的时候被坑了。(所以说平时写代码的时候分层一定要按照规矩来。) 我使用的解决方法也挺简单,就是在model 里面需要使用的地方才 import,这样就解决了这个问题。但是,解决问题不是我的重点,我更关注的是为什么会出现循环引入,为什么会报错,面对这种情况比较优雅的处理情况是什么。 首先,我的简便解决方法是:

//model.py

class ModelXXX():
    ....

    def save():
        from services import XXXService
        XXXService.xxxx()

//services.py
from model import ModelXXX

class XXXService():
    ...
    def yyy():
        ModelXXX()


那接下来是解答我自己的疑惑的时候了

为什么因为分层不合理,导致代码直接的依赖混乱,从而导致了互相依赖。

既然我们将 import 放到函数里面就不会报错了,而放到外面就报错,这是为什么?经过谷歌之后,我有了一些答案。首先要从 import 的特性说起: 在python 中,import 一个模块的时候会启动这个模块中的顶层代码,例如我们上面的代码,在model.py 中 import services 的时候,会执行里面的 from model import Modelxxx,这样的话又回到import model 了,因为这时 model 和 service 大家谁都还没有引入,所以就用引发 import 错误。 那如果在 services.py 中将 import 放到函数里面,那么在 Models 中 import servicve 时,因为不会执行函数,所以就没有 import 操作,从而 model 是可以成功被 load 进python 的执行环境的,当指定到 service 的函数的时候,此时 model 已经在执行环境中,所以就不需要跑一遍了,从而就不会报错了。

首先先谈谈解决方法,一般来说,解决的方法主要是:

但是,这些都是在出现循环引入之后的解决措施,其实,最优雅的解决方法还是重构代码,去除循环引入,一来避免的循环引入,二来使代码结构更加合理,优美。

参考资料