循环进口地狱

时间:2015-11-13 11:10:57

标签: python import circular-dependency

Python是非常优雅的语言。好吧,除了......除了进口。我仍然无法以我看来自然的方式工作。

我的文件MyObjectA中有一个班级mypackage/myobjecta.py。该对象使用mypackage/utils.py中的一些实用程序函数。所以我在myobjecta.py的第一行写道:

from mypackage.utils import util_func1, util_func2

但是一些实用程序函数创建并返回MyObjectA的新实例。所以我需要写utils.py

from mypackage.myobjecta import MyObjectA

嗯,不,我不能。这是一个循环导入,Python将拒绝这样做。

关于这个问题,这里有很多问题,但似乎没有人给出满意的答案。从我在所有答案中可以阅读的内容:

  1. 重新组织你的模块,你做错了!但是我不知道 即使在像我这样简单的情况下,如何更好地组织我的模块 呈现。
  2. 只试用import ...而不是from ... import ... (我个人不喜欢写作并可能完全重构 姓名限定词;我喜欢看到我正在进口的内容 来自外界的模块)。那会有帮助吗?我不确定, 还有循环进口。
  3. 在你使用其他模块的东西之前,做一些黑客只是在函数体的内部范围内导入一些东西。
  4. 我仍然希望有解决方案4)这将是Pythonic在功能和优雅,简单和工作的意义上。或者没有?

    注意:我主要是一个C ++程序员,通过包含相应的标题可以很容易地解决上面的例子,我不相信它在Python中是不可能的。

4 个答案:

答案 0 :(得分:1)

Pythonistas不赞成从函数中导入。 Pythonista通常不喜欢全局变量。但是,我看到了两者,并且认为使用它们的项目不会比某些严格的Pythhonistas所做的其他项目差。该功能确实存在,对其功能没有过多争论。

从函数导入问题还有另一种选择:从文件的顶部(或者实际上是从底部)导入时,此导入将花费一些时间(虽然很短,但是有些时间),但是Python将缓存整个文件,如果另一个文件需要相同的导入,Python可以快速检索模块而无需导入。而如果从函数中导入,则情况会变得很复杂:每次调用该函数时,Python都必须处理import行,这可能会以很小的方式减慢程序速度。

对此的一种解决方案是独立缓存模块。好的,这使用函数体内的导入和全局变量。哇!

_MODULEA = None

def util1():
    if _MODULEA is None:
        from mymodule import modulea as _MODULEA

    obj = _MODULEA.ClassYouWant
    return obj

我看到在使用平面API的项目中采用了这种策略。不管您是否喜欢(我不确定自己),它都可以运行且非常快捷,因为导入行仅执行一次(函数首次执行时)。不过,我还是建议进行重组:循环导入问题通常显示出结构问题,而这始终值得解决。不过,我确实同意,如果在这种情况下Python提供了更多有用的错误,那就太好了。

答案 1 :(得分:0)

在函数体中导入某些内容并不是什么骇人听闻,这是一种绝对有效的模式:

def some_function():
    import logging
    do_some_logging()

通常只会引发ImportError,因为import()在调用时会评估整个文件的顶级语句。

如果您没有逻辑循环依赖... ,在python中没有什么是不可能的......

如果您确实希望将导入放在最上面

,可以采取一种方法

来自David Beazleys的优秀演讲Modules and Packages: Live and Let Die! - PyCon 20151:54:00,这是一种处理python循环导入的方法:

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']

这会尝试导入SimplifiedImageSerializer,如果引发ImportError,因为它已经导入,它会从导入缓存中提取它。

PS:你必须以David Beazley的声音阅读整篇文章。

答案 2 :(得分:0)

不要将mypackage.utils导入您的主模块,它已存在于mypackage.myobjecta中。导入mypackage.myobjecta后,正在执行该模块中的代码,并且您不需要将任何内容导入当前模块,因为mypackage.myobjecta已经完成。

答案 3 :(得分:0)

无法实现您想要的。 Python无法知道要执行顶层代码才能执行您要执行的操作的顺序。

假设您首先导入utils。 Python将首先评估第一条语句from mypackage.myobjecta import MyObjectA,该语句需要执行myobjecta模块的顶层。然后,Python必须执行from mypackage.utils import util_func1, util_func2,但是直到解析myobjecta导入后,它才能执行该操作。

Python不会无限递归,而是通过允许最内部的导入完成而无需完成来解决这种情况。因此,utils导入完成而没有执行文件的其余部分,并且您的import语句失败,因为util_func1还不存在。

import myobjecta起作用的原因是,它允许在执行了每个模块的主体之后稍后再解析符号。就个人而言,即使是这种循环导入,也让我感到困惑,因此,我不建议您完全使用它们。

如果您还是想使用循环导入,并且希望它们是“从”导入,那么我认为它可以可靠地工作的唯一方法是:在从另一个模块导入之前,定义另一个模块使用的所有符号。在这种情况下,您对util_func1util_func2的定义必须在from mypackage.myobjecta import MyObjectA中的utils语句之前,而MyObjectA的定义必须在{{1 }}在from mypackage.utils import util_func1, util_func2中。

像C#这样的编译语言可以处理这种情况,因为顶层是定义的集合,而不是指令。他们不必按给定的顺序创建每个类和每个函数。他们可以按照需要的顺序解决问题,从而避免出现任何周期。 (C ++通过在原型中复制信息来做到这一点,我个人认为这是一个非常棘手的解决方案,但那也不是Python的工作方式。)

像Python这样的系统的优点是它是高度动态的。是的,您可以基于仅在运行时才知道的内容来不同地定义类或函数。或在创建类后修改它。或者尝试导入依赖项,如果不可用,则不使用它们。如果您不觉得坚持严格的依赖关系树不便带来这些麻烦,那是完全合理的,也许编译语言可以更好地为您服务。