Python 模块加载机制导致的问题
Python 在导入模块时,会执行模块中的代码。当存在互相依赖(循环依赖)时,如果在模块顶层进行导入,可能会出现问题。因为在一个模块还未完全加载和初始化时,另一个模块就尝试导入它,这可能会导致某些对象或属性还未定义。
module_a.py
from module_b import B
class A:
def __init__(self):
self.b = B()
a = A()
module_b.py
from module_a import A
class B:
def __init__(self):
self.a = A()
b = B()
当 Python 解释器执行 import module_a 时,会先执行 module_a.py 中的代码。在执行到 from module_b import B 时,会去加载 module_b.py。而 module_b.py 中又有 from module_a import A,此时 module_a 还未完全加载完毕,A 类可能还未定义,就会导致循环依赖错误。
函数内导入的延迟加载特性
将导入语句放在函数内部,导入操作会延迟到函数被调用时才执行。这样就避免了在模块加载阶段就出现循环依赖的问题。
module_a.py
class A:
def __init__(self):
from module_b import B
self.b = B()
a = A()
module_b.py
class B:
def __init__(self):
from module_a import A
self.a = A()
b = B()
在这个修改后的例子中,当 module_a.py 被加载时,并不会立即执行 from module_b import B,而是在 A 类的 init 方法被调用时才执行。同理,module_b.py 中的导入操作也是延迟执行的。这样,每个模块在加载时都不会尝试去导入还未完全加载的模块,从而避免了循环依赖问题
注意事项
虽然将导入语句放在函数内部可以解决循环依赖问题,但这可能会影响代码的可读性和性能。因为每次调用函数时都会执行导入操作,可能会带来一些额外的开销。更好的做法是重构代码,避免循环依赖的产生。
pydeps
https://knowledge.zhaoweiguo.com/build/html/lang/pythons/tools/code_analysis/pydeps
https://pypi.org/project/pydeps/
brew install graphviz
pip install pydeps
pydeps xx.py --show-deps
会用图的方式检测出互相依赖