我正在使用一个包含大约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__)
但这看起来真的很讨厌,必须有更好的方法去做,对吧?
答案 0 :(得分:6)
为了避免更改每个模块,您可以将导入跟踪功能放在import hook中,或者放在自定义__import__
中,您可以将其粘贴在内置插件中 - 后者,一次,可能会更好,因为__import__
被调用,即使导入的模块已经在sys.modules
,这是在循环导入期间的情况。
对于实现,我只是使用“在导入过程中”的一组模块,类似于(benjaoming edit:插入源自原始的工作片段):
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
答案 1 :(得分:1)
并非所有循环导入都是一个问题,正如您在未抛出异常时发现的那样。
当它们出现问题时,下次尝试运行任何测试时都会出现异常。发生这种情况时,您可以更改代码。
我认为这种情况不需要做任何改变。
什么时候不成问题的例子:
import b
a = 42
def f():
return b.b
import a
b = 42
def f():
return a.a
答案 2 :(得分:1)
import
使用__builtin__.__import__()
,因此如果你monkeypatch那么每个地方的所有导入都会获取更改。请注意,循环导入不必然是一个问题。
答案 3 :(得分:1)
Python中的循环导入与PHP包含的不同。
Python导入的模块首次加载到导入“处理程序”中,并在此过程中保留。对于每个后续导入,此处理程序为本地命名空间中的名称分配从该模块导入的内容。模块是唯一的,对该模块名称的引用将始终指向相同的已加载模块,无论它在何处导入。
因此,如果您有循环模块导入,则每个文件的加载将发生一次,然后每个模块将具有与在其命名空间中创建的其他模块相关的名称。
当引用两个模块中的特定名称时当然会出现问题(当循环导入发生在相反模块的导入中引用的类/函数定义之前)时,如果出现这种情况,则会出现错误发生的情况。