- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一个结构如下的 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 文档中的错误。更多信息可用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 __init__
文件中导入 A
,删除该导入将解决问题(您的程序稍后可以执行 import level1.level2.a.A
),但是如果你的包变得更复杂,你会看到错误再次出现。
Python 有时可以很好地使这些复杂的导入工作,但它们何时工作和不工作的规则一点也不直观。一个通用规则是 from xxx.yyy import zzz
比 import xxx.yyy
后跟 xxx.yyy.zzz
更宽容。在后一种情况下,解释器必须在检索 xxx.yyy.zzz
时完成将 yyy
绑定(bind)到 xxx
命名空间,但在前一种情况下,解释器可以在顶级包命名空间完全设置之前遍历包中的模块。
所以对于这个例子,真正的问题是 a.py
中的裸导入这很容易修复:
from level1.level2.base import Base
class A(Base): pass
始终使用相对导入是强制使用 from ... import
的好方法,原因很简单,如果没有 from',相对导入将无法工作。要在上面的示例中使用相对导入,
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
关于python - 包内导入并不总是有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32856252/
我在一个 twig 模板中使用 Assetic 来指定要从我的包中使用的 2 个 JS 文件 { % javascripts '@JiraExtendedReportsBund
我正在做一个 VS 包,它在菜单中有一个 DynamicItemStart 按钮。我在 VS 启动时加载动态按钮的内容没有任何问题,但我试图在某些事件(例如打开项目)之后向其内容添加更多命令。我将新命
需求是从plsql调用java方法,我可以通过loadjava命令来实现它。我遵循的步骤是: 第 1 步:创建 Java Class/jar 文件并将其放置在 Unix 机器上 第2步:将Java C
我是一名优秀的程序员,十分优秀!