我有一个像这样结构的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",尚未完成执行。经典循环导入。
所以这应该表明我的代码结构很差,需要重新设计以避免循环依赖。
有什么选择呢?
我能想到的一些解决方案,以及为什么我不想使用它们:
所以我的问题不仅仅是如何避免循环导入,而是以与我试图实现的方式一样干净(如果不是更干净)的方式这样做。
有没有人有更好的方法?
答案 0 :(得分:4)
修改强>
我已经对此进行了更彻底的研究,并得出结论,这是核心Python或Python文档中的错误。有关详细信息,请at this question and answer。
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 A
文件中导入__init__
,则删除该导入将解决问题(并且您的程序稍后可以执行import level1.level2.a.A
),但是如果你的软件包变得越来越复杂,你会再次看到错误。
Python有时可以很好地使这些复杂的导入工作,并且它们何时能够工作和不工作的规则根本不直观。一般规则是from xxx.yyy import zzz
可能比import xxx.yyy
更宽容,然后是xxx.yyy.zzz
。在后一种情况下,解释器必须在检索yyy
时将xxx
绑定到xxx.yyy.zzz
命名空间,但在前一种情况下,解释器可以遍历模块完全设置顶级包命名空间之前的包。
因此,对于这个例子,真正的问题是a.py
中的裸导入。这很容易修复:
from level1.level2.base import Base
class A(Base): pass
持续使用相对导入是强制使用from ... import
的一种好方法,原因很简单,相对导入在from'. To use relative imports with the example above,
level1 / level2 / a.py`应该包含的情况下不起作用:< / p>
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