首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python3:从子模块隐式导入,避免递归导入

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

Stack Overflow用户
提问于 2019-08-02 10:56:50
回答 1查看 261关注 0票数 3

想象一下下面的场景。您正在编写由多个文件组成的模块。在编写代码时,最终会遇到多个文件(例如main.pyside.py)相互导入的情况,从而导致递归导入,即禁止导入。

代码语言:javascript
复制
module/
  main.py
  side.py

您决定将main拆分为多个文件:base.pyadvanced.py。前者仅包含来自main.py的基本定义,不使用side或任何其他子模块;其他希望使用main的子模块应该满足于导入该子模块。后者(advanced.py)可以自由地从side导入任何东西,但是由于side不使用advanced,所以不存在递归。这将解析递归导入。

现在,您将看到以下包结构:

代码语言:javascript
复制
module/
  side.py
  base.py
  advanced.py

baseadvanced放入子文件夹main (从而创建子模块)是有意义的,因为这至少部分保留了原始的包结构。这样我们就可以得到

代码语言:javascript
复制
module/
  side.py
  main/
    base.py
    advanced.py

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

代码语言:javascript
复制
from .main import *

main的接口被前面提到的“递归修复”操作符打破了。那么,如何恢复原始接口,即如何使from .main import *baseadvanced导入所有内容?

示例

原始main.py

代码语言:javascript
复制
from .side import *

class A:
  pass

class B(C):
  pass

原始side.py

代码语言:javascript
复制
from .main import *

class C:
  pass

class D(A):
  pass

重组后,main被分成两个文件:

代码语言:javascript
复制
# base.py
class A:
  pass
代码语言:javascript
复制
# advanced.py
from module.side import *

class B(C):
  pass

side现在应该导入main.base而不是main

代码语言:javascript
复制
# new side.py
from .main.base import *

class C:
  pass

class D(A):
  pass

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

__init__.py shenaningans

__init__.py子模块中创建main,并将以下内容放入其中(来自Implicit import from submodules的解决方案):

代码语言:javascript
复制
from .base import *
from .advanced import *

这方面的问题是再次引入一个递归导入:回想一下side使用了main.base,因此在某个地方有一行代码

代码语言:javascript
复制
from .main.base import *

这将调用__init__.py,导致side也从advanced导入。这是我们想避免的。更具体地说,将以下内容放入python解释器中

代码语言:javascript
复制
from module.side import *

输出以下错误:

代码语言:javascript
复制
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所需的。

相反,将shenaningans放在all.py

与其将上述两行代码放入__init__.py中,不如将其放入另一个文件all.py中。现在,当一个人想从main导入所有东西时,他会写

代码语言:javascript
复制
from .main.all import *

这仍然与from .main import *不一样,因此每当对包进行“递归修复”重组时,就必须查找所有这些导入并重写它们(通过附加.all)。

EN

回答 1

Stack Overflow用户

发布于 2022-03-04 19:33:05

在许多情况下,这种方法有效。要在块中导入模块的对象,例如,如果side.py使用main.A,而main.py使用inside .c,则可以在每个模块中定义简单函数,并在每个模块中导入A或C,函数返回C或A类。

在main.py中:

代码语言:javascript
复制
def get_C():
    from .side import C
    return C

class A:
  pass

class B(get_C()):
  pass

在side.py中:

代码语言:javascript
复制
def get_A():
    from .main import A
    return A

class C:
  pass

class D(get_A()):
  pass

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57325119

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档