包裹内进口并不总是有效 [英] Intra-package imports do not always work

查看:62
本文介绍了包裹内进口并不总是有效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个结构如下的Django项目:

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

...其中appname / models / __ init__.py仅包含如下语句:

  from appname.models.base import Base来自appname.models.a导入的
来自appname.models.c导入的
C

...,其中appname / models / base.py包含:

  import django.db.models 


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

,其中appname / models / a.py包含:

 导入appname.models作为模型


A类(模型。基础):
....

...以及类似的appname / models / c.py等。



我对我的代码结构很满意,但是当然,由于循环曝光,它不起作用



运行appname / __ init__.py时,appname / models / a.py将运行,但是该模块导入 appname.models,但没有完成执行了。经典的循环导入。



因此,这可能表明我的代码结构不良,需要重新设计以避免循环依赖。



有哪些选择呢?



我能想到的一些解决方案,然后为什么呢?不想使用它们:


  1. 将我所有的模型代码合并到一个文件中:在同一文件中有20多个类会更糟我认为,样式要比我尝试执行的样式(使用单独的文件)。

  2. 将基本模型类移动到应用程序名称/模型之外的另一个包中:这意味着我将在我的项目中最终获得包含基本/父类的包,理想情况下,应将它们拆分为子/子类所在的包。除了避免循环导入之外,为什么我应该在同一程序包中而不是在自己的程序包(子/子类所在的位置)中具有模型,表单,视图等的基类/父类?

所以我的问题不只是如何避免循环进口,而且要以一种与清洁(如果不是清洁的话)一样清洁的方式做到这一点。我尝试实施。



有人有更好的方法吗?

解决方案

编辑



我对此进行了更深入的研究,得出的结论是,这是Python核心或Python文档中的错误。有关此问题和答案的更多信息,请



Python的PEP 8明确表示绝对优先于相对进口。此问题的解决方法涉及相对导入,并且导入机制中可能有解决方法。



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



原始答案



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



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



level1 / __ init __。py

 从level1.level2导入基础

level1 / level2 / __ init __。py

  from level1.level2。基本导入从level1.level2.a导入的基本
a

level1 / level2 / a.py

 导入level1.level2.base 
A类(level1.level2.base.Base):通过

level1 / level2 /基本

 类基本:通过

可以用几种不同的方式修复该错误(对于这种小情况),但是许多潜在的修复都是脆弱的。例如,如果您不需要在2级 __ init __ 文件中导入 A ,则删除该导入即可解决问题(您的程序以后可以执行 import level1.level2.aA ),但是如果您的程序包变得更加复杂,您将看到错误再次蔓延。



Python有时可以很好地使这些复杂的导入生效,并且何时生效和不生效的规则完全不直观。一个一般规则是,从中获取比从 import xxx.yyy 后跟 xxx.yyy.zzz 。在后一种情况下,解释器必须在需要检索<时将 yyy 绑定到 xxx 命名空间中。 code> xxx.yyy.zzz ,但在前一种情况下,解释器可以在顶级包名称空间完全建立之前遍历包中的模块。



因此,在此示例中,真正的问题是 a.py 中的裸导入很容易解决:

$从level1.level2.base导入的b
$ b

  / pre> 

始终使用相对进口是对...强制使用 from ... import 的一种好方法如果没有 from',相对导入就无法工作的简单原因。要在上面的示例中使用相对导入, level1 / level2 / a.py`应该包含:

 从.base import基础
A类(基础):通过

这可以解决问题导入周期,其他一切正常。如果导入的名称(例如Base)在没有源模块名称的前缀时过于通用,那么可以在导入时轻松对其重命名:

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

尽管可以解决当前问题,但是如果程序包的结构变得更加复杂,则可能需要考虑更广泛地使用相对导入。例如, level1 / level2 / __ init __。py 可以是:

  from .base import Base 
from .a import A


I have a Django project structured like so:

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

... where appname/models/__init__.py contains only statements like so:

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

... and where appname/models/base.py contains:

import django.db.models


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

and where appname/models/a.py contains:

import appname.models as models


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

...and similarly for appname/models/c.py, etc..

I am quite happy with this structure of my code, but of course it does not work, because of circular imports.

When appname/__init__.py is run, appname/models/a.py will get run, but that module imports "appname.models", which has not finished executing yet. Classic circular import.

So this supposedly indicates that my code is structured poorly and needs to be re-designed in order to avoid circular dependency.

What are the options to do that?

Some solutions I can think of and then why I don't want to use them:

  1. Combine all my model code into a single file: Having 20+ classes in the same file is a far worse style than what I am trying to do (with separate files), in my opinion.
  2. Move the "Base" model class into another package outside of "appname/models": This means that I would end up with package in my project that contains base/parent classes that should ideally be split into the packages in which their child/sub classes are located. Why should I have base/parent classes for models, forms, views, etc. in the same package and not in their own packages (where the child/sub classes would be located), other than to avoid circular imports?

So my question is not just how to avoid circular imports, but to do so in a way that is just as clean (if not cleaner) that what I tried to implement.

Does anyone have a better way?

解决方案

Edit

I have researched this more thoroughly and come to the conclusion that this is a bug in either core Python or the Python documentation. More information is available at this question and answer.

Python's PEP 8 indicates a clear preference for absolute over relative imports. This problem has a workaround that involves relative imports, and there is a possible fix in the import machinery.

My original answer below gives examples and workarounds.

Original answer

The problem, as you have correctly deduced, is circular dependencies. In some cases, Python can handle these just fine, but if you get too many nested imports, it has issues.

For example, if you only have one package level, it is actually fairly hard to get it to break (without mutual imports), but as soon as you nest packages, it works more like mutual imports, and it starts to become difficult to make it work. Here is an example that provokes the error:

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

The error can be "fixed" (for this small case) in several different ways, but many potential fixes are fragile. For example, if you don't need the import of A in the level2 __init__ file, removing that import will fix the problem (and your program can later execute import level1.level2.a.A), but if your package gets more complex, you will see the errors creeping in again.

Python sometimes does a good job of making these complex imports work, and the rules for when they will and won't work are not at all intuitive. One general rule is that from xxx.yyy import zzz can be more forgiving than import xxx.yyy followed by xxx.yyy.zzz. In the latter case, the interpreter has to have finished binding yyy into the xxx namespace when it is time to retrieve xxx.yyy.zzz, but in the former case, the interpreter can traverse the modules in the package before the top-level package namespace is completely set up.

So for this example, the real problem is the bare import in a.py This could easily be fixed:

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

Consistently using relative imports is a good way to enforce this use of from ... import for the simple reason that relative imports do not work without the from'. To use relative imports with the example above,level1/level2/a.py` should contain:

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

This breaks the problematic import cycle and everything else works fine. If the imported name (such as Base) is too confusingly generic when not prefixed with the source module name, you can easily rename it on import:

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

Although that fixes the current problem, if the package structure gets more complex, you might want to consider using relative imports more generally. For example, level1/level2/__init__.py could be:

from .base import Base
from .a import A

这篇关于包裹内进口并不总是有效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆