想象一下以下场景。您正在编写由多个文件组成的某个模块。当您编写代码时,最终会遇到多个文件(假设main.py
和side.py
)相互导入的情况,从而导致递归导入,这是不可能的。
module/
main.py
side.py
您决定将main
拆分为多个文件:base.py
和advanced.py
。前者仅包含main.py
中的基本定义,并且不使用side
或任何其他子模块;其他希望使用main
的子模块应该满足于导入此定义。后者(advanced.py
)可以自由地从side
导入任何内容,但由于side
不使用advanced
,因此不存在递归。这将解析递归导入。
现在,您只剩下以下包结构:
module/
side.py
base.py
advanced.py
将base
和advanced
放入子文件夹main
(从而创建子模块)是有意义的,因为这至少部分保留了原始的包结构。因此,我们得到
module/
side.py
main/
base.py
advanced.py
但现在考虑第三个文件third.py
,它最初导入了整个main
:
from .main import *
到main
的接口被前面提到的"递归修复"操作符中断。那么,如何恢复原来的界面,即如何使from .main import *
同时从base
和advanced
导入所有内容?
示例
原始main.py
:
from .side import *
class A:
pass
class B(C):
pass
原始side.py
:
from .main import *
class C:
pass
class D(A):
pass
重组后main
拆分成两个文件:
# base.py
class A:
pass
# advanced.py
from module.side import *
class B(C):
pass
和side
现在应该导入main.base
而不是main
:
# new side.py
from .main.base import *
class C:
pass
class D(A):
pass
以下是一些不完全令人满意的解决方案/想法:
__init__.py
申宁根
在main
子模块中创建__init__.py
,并将以下内容放入其中(来自Implicit import from submodules的解决方案):
from .base import *
from .advanced import *
这样做的问题是再次引入了递归导入:回想一下side
使用main.base
,因此在某个地方有一行代码
from .main.base import *
将调用__init__.py
,导致side
也从advanced
导入。这是我们想避免的。更具体地说,将以下内容放入python解释器
from module.side import *
输出以下错误:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".../module/side.py", line 2, in <module>
from .main.base import *
File ".../module/main/__init__.py", line 2, in <module>
from .advanced import *
File ".../module/main/advanced.py", line 4, in <module>
class B(C):
NameError: name 'C' is not defined
原因是在加载advanced
时,它试图加载side
。但由于side
是"已加载的"(或者更确切地说,是当前正在加载的),为了避免无限递归,Python只是跳过它。但不加载advanced
所需的类C
。
改为将shenaningans放入all.py
不是将上述两行代码放入__init__.py
,而是将其放入另一个文件all.py
。现在,当用户想要从main
导入所有内容时,可以写为
from .main.all import *
这仍然与from .main import *
不同,因此每当发生包的这种"递归修复"重构时,必须查找所有此类导入并重写它们(通过追加.all
)。
推荐答案
在许多情况下,此方法都有效。例如,如果side.py使用main.A,main.py使用side.C,则可以在每个模块中定义简单函数,并在其中导入A或C,该函数返回C或A类。
在main.py中:
def get_C():
from .side import C
return C
class A:
pass
class B(get_C()):
pass
和side.py中:
def get_A():
from .main import A
return A
class C:
pass
class D(get_A()):
pass
在这种情况下,不会发生循环导入错误
相关推荐
最新文章