繁体   English   中英

包内进口并不总是有效

[英]Intra-package imports do not always work

我有一个像这样结构的Django项目:

appname/
   models/
      __init__.py
      a.py
      base.py
      c.py

... appname / models / __ init__.py只包含这样的语句:

from appname.models.base import Base
from appname.models.a import A
from appname.models.c import C

...以及appname / models / base.py包含的位置:

import django.db.models


class Base(django.db.models.Model):
   ...

以及appname / models / a.py包含的位置:

import appname.models as models


class A(models.Base):
   ....

...同样适用于appname / models / c.py等。

我很满意我的代码结构,但当然它不起作用,因为循环导入。

当appname / __ init__.py运行时,appname / models / a.py将运行,但该模块导入“appname.models”,尚未完成执行。 经典循环导入。

所以这应该表明我的代码结构很差,需要重新设计以避免循环依赖。

有什么选择呢?

我能想到的一些解决方案,以及为什么我不想使用它们:

  1. 将我的所有模型代码合并到一个文件中:在我看来,在同一个文件中有20多个类比我尝试做的(使用单独的文件)更糟糕。
  2. 将“Base”模型类移动到“appname / models”之外的另一个包中:这意味着我最终会在我的项目中包含包含基类/父类的包,这些类理想情况下应该拆分为其子/子包中的包课程位于。 为什么我应该在同一个包中有模型,表单,视图等的基类/父类,而不是它们自己的包(子/子类所在的位置),除了避免循环导入?

所以我的问题不仅仅是如何避免循环导入,而是以一种与我试图实现的方式一样干净(如果不是更干净)的方式。

有没有人有更好的方法?

编辑

我已经对此进行了更彻底的研究,并得出结论,这是核心Python或Python文档中的错误。 此问题和答案提供更多信息。

Python的PEP 8表明绝对优先于绝对超过相对进口。 此问题有一个涉及相对导入的解决方法,并且导入机制中可能存在修复。

我在下面的原始答案提供了示例和解决方法。

原始答案

正如您所正确推断的那样,问题是循环依赖。 在某些情况下,Python可以很好地处理这些,但是如果你得到太多的嵌套导入,它就会出现问题。

例如,如果你只有一个包级别,实际上很难让它破坏(没有相互导入),但是一旦你嵌套包,它就更像是相互导入,并且开始变得难以制作这行得通。 这是一个引发错误的示例:

level1/__init__.py

    from level1.level2 import Base

level1/level2/__init__.py

    from level1.level2.base import Base
    from level1.level2.a import A

level1/level2/a.py

    import level1.level2.base
    class A(level1.level2.base.Base): pass

level1/level2/base

    class Base: pass

错误可以通过几种不同的方式“修复”(对于这个小案例),但许多潜在的修复都很脆弱。 例如,如果您不需要在level2 __init__文件中导入A ,则删除该导入将解决问题(并且您的程序稍后可以执行import level1.level2.aA ),但如果您的包变得更复杂,那么将再次看到错误蔓延。

Python有时可以很好地使这些复杂的导入工作,并且它们何时能够工作和不工作的规则根本不直观。 一般规则是from xxx.yyy import zzz可以比import xxx.yyy更加宽容,然后是xxx.yyy.zzz 在后一种情况下,解释器必须在检索xxx.yyy.zzz时完成将yyy绑定到xxx命名空间,但在前一种情况下,解释器可以在顶层包之前遍历包中的模块命名空间已完全设置。

所以对于这个例子,真正的问题是a.py的裸导入这很容易修复:

    from level1.level2.base import Base
    class A(Base): pass

始终使用相对导入是强制使用from ... import一种好方法,原因很简单,相对导入在没有from'. To use relative imports with the example above,情况下不起作用from'. To use relative imports with the example above, from'. To use relative imports with the example above, level1 / level2 / a.py`应包含:

from .base import Base
class A(Base): pass

这打破了有问题的导入周期,其他一切正常。 如果导入的名称(例如Base)在没有以源模块名称作为前缀时过于混乱,则可以在导入时轻松地重命名:

from .base import Base as BaseModel
class A(BaseModel): pass

虽然这解决了当前的问题,但如果包结构变得更复杂,您可能需要考虑更普遍地使用相对导入。 例如, level1/level2/__init__.py可以是:

from .base import Base
from .a import A

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM