23

我正在处理一个包含大约 30 个独特模块的项目。它设计得不太好,所以在向项目添加一些新功能时,我通常会创建循环导入。

当然,当我添加循环导入时,我并不知道。有时很明显,当我收到AttributeError: 'module' object has no attribute 'attribute'我明确定义的错误时,我已经进行了循环导入'attribute'。但其他时候,代码不会因为使用方式而引发异常。

所以,对于我的问题:

是否可以以编程方式检测循环导入发生的时间和地点?

到目前为止,我能想到的唯一解决方案是拥有一个importTracking包含 dict的模块importingModules,一个函数importInProgress(file),它递增importingModules[file],如果它大于 1 则抛出错误,以及一个函数importComplete(file)递减importingModules[file]。所有其他模块如下所示:

import importTracking
importTracking.importInProgress(__file__)
#module code goes here.
importTracking.importComplete(__file__)

但这看起来真的很讨厌,必须有更好的方法来做到这一点,对吧?

4

4 回答 4

10

为了避免更改每个模块,您可以将导入跟踪功能粘贴在import hook中,或者在自定义中__import__粘贴您可以粘贴在内置插件中 - 后者可能会更好地工作,因为__import__即使在导入的模块已经在 中sys.modules,循环导入就是这种情况。

对于实现,我只需使用“正在导入的过程中”的一组模块,例如(benjaoming 编辑:插入源自原始的工作片段):

beingimported = set()
originalimport = __import__
def newimport(modulename, *args, **kwargs):
    if modulename in beingimported:
        print "Importing in circles", modulename, args, kwargs
        print "    Import stack trace -> ", beingimported
        # sys.exit(1) # Normally exiting is a bad idea.
    beingimported.add(modulename)
    result = originalimport(modulename, *args, **kwargs)
    if modulename in beingimported:
        beingimported.remove(modulename)
    return result
import __builtin__
__builtin__.__import__ = newimport
于 2010-03-09T01:22:03.627 回答
1

并非所有循环导入都是问题,正如您在未引发异常时发现的那样。

当它们成为问题时,下次尝试运行任何测试时都会出现异常。发生这种情况时,您可以更改代码。

我认为这种情况不需要任何改变。

不存在问题的示例:

一个.py

import b
a = 42
def f():
  return b.b

b.py

import a
b = 42
def f():
  return a.a
于 2010-03-09T01:19:55.097 回答
1

Python中的循环导入与 PHP 包含的不同。

Python 导入的模块第一次加载到导入“处理程序”中,并在整个过程中保留在那里。此处理程序为从该模块导入的任何内容分配本地名称空间中的名称,用于每次后续导入。一个模块是唯一的,对该模块名称的引用将始终指向同一个加载的模块,无论它是从哪里导入的。

因此,如果您有一个循环模块导入,每个文件的加载将发生一次,然后每个模块将具有与创建到其命名空间中的另一个模块相关的名称。

在两个模块中引用特定名称时当然可能会出现问题(当循环导入发生在相对模块的导入中引用的类/函数定义之前),但如果发生这种情况,您会收到错误消息。

于 2010-03-09T01:28:47.823 回答
0

import使用__builtin__.__import__(),因此如果您使用monkeypatch ,那么每个地方的每个导入都会获取更改。请注意,循环导入不一定是问题。

于 2010-03-09T01:20:21.333 回答