[英]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”,尚未完成执行。 经典循环导入。
所以这应该表明我的代码结构很差,需要重新设计以避免循环依赖。
有什么选择呢?
我能想到的一些解决方案,以及为什么我不想使用它们:
所以我的问题不仅仅是如何避免循环导入,而是以一种与我试图实现的方式一样干净(如果不是更干净)的方式。
有没有人有更好的方法?
编辑
我已经对此进行了更彻底的研究,并得出结论,这是核心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.