Python3:从子模块隐式导入,避免递归导入

2024-01-06

想象一下以下场景。您正在编写一些由多个文件组成的模块。当您编写代码时,最终您会遇到多个文件(比方说main.py and side.py) 互相导入,导致递归导入,这是不行的。

module/
  main.py
  side.py

你决定分手main分成多个文件:base.py and advanced.py。前者仅包含基本定义main.py并且不利用side也没有任何其他子模块;希望使用的其他子模块main导入这个应该感到满意。后者 (advanced.py)可以自由地导入任何东西side, 但是由于side不利用advanced,不存在递归。这解决了递归导入问题。

现在,您剩下以下包结构:

module/
  side.py
  base.py
  advanced.py

这是有道理的base and advanced进入子文件夹main(从而创建一个子模块),因为这至少部分保留了原始的包结构。这样我们就得到

module/
  side.py
  main/
    base.py
    advanced.py

但现在考虑第三个文件third.py,最初导入了整个main:

from .main import *

接口为main被前面提到的“递归修复”运算符破坏了。那么,如何恢复原来的界面,也就是如何制作from .main import *从两者导入所有内容base and advanced?


Example

原来的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

And side现在应该正在导入main.base代替main:

# new side.py
from .main.base import *

class C:
  pass

class D(A):
  pass

以下是一些不完全令人满意的解决方案/想法:

__init__.py恶作剧

Create __init__.py in the main子模块,并将以下内容放入其中(解决方案来自从子模块隐式导入 https://stackoverflow.com/questions/11325867/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 只是跳过它。但接下来上课C,这是由advanced,未加载。

而是把恶作剧放进去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

在这种情况下,不会发生循环导入错误

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python3:从子模块隐式导入,避免递归导入 的相关文章

随机推荐