在Python中,当两个模块试图相互导入时会发生什么?更一般地说,如果多个模块试图在一个循环中导入会发生什么?
另见我能做什么关于“ImportError:不能导入名称X”或“AttributeError:…”(很可能是由于循环导入)”?关于可能导致的常见问题,以及如何重写代码以避免此类导入的建议。参见为什么循环导入看起来在调用堆栈中更上一层,但随后在更下一层引发ImportError ?有关问题发生的原因和方式的技术细节。
在Python中,当两个模块试图相互导入时会发生什么?更一般地说,如果多个模块试图在一个循环中导入会发生什么?
另见我能做什么关于“ImportError:不能导入名称X”或“AttributeError:…”(很可能是由于循环导入)”?关于可能导致的常见问题,以及如何重写代码以避免此类导入的建议。参见为什么循环导入看起来在调用堆栈中更上一层,但随后在更下一层引发ImportError ?有关问题发生的原因和方式的技术细节。
当前回答
循环导入会终止,但在模块初始化期间,您需要注意不要使用循环导入的模块。
考虑以下文件:
a.py:
print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules, )
import b
print "a out"
b.py:
print "b in"
import a
print "b out"
x = 3
如果你执行a.py,你会得到以下结果:
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out
在第二次导入b.py时(在第二个ain中),Python解释器不会再次导入b,因为它已经存在于模块dict中。
如果你试图在模块初始化期间从a访问b.x,你会得到一个AttributeError。
将下面的行追加到a.py:
print b.x
则输出为:
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
Traceback (most recent call last):
File "a.py", line 4, in <module>
import b
File "/home/shlomme/tmp/x/b.py", line 2, in <module>
import a
File "/home/shlomme/tmp/x/a.py", line 7, in <module>
print b.x
AttributeError: 'module' object has no attribute 'x'
这是因为模块是在导入时执行的,在访问b.x时,x = 3行还没有执行,这只会在b退出后发生。
其他回答
Ok, I think I have a pretty cool solution. Let's say you have file a and file b. You have a def or a class in file b that you want to use in module a, but you have something else, either a def, class, or variable from file a that you need in your definition or class in file b. What you can do is, at the bottom of file a, after calling the function or class in file a that is needed in file b, but before calling the function or class from file b that you need for file a, say import b Then, and here is the key part, in all of the definitions or classes in file b that need the def or class from file a (let's call it CLASS), you say from a import CLASS
这是可行的,因为您可以导入文件b,而无需Python执行文件b中的任何导入语句,因此您可以避免任何循环导入。
例如:
文件:
class A(object):
def __init__(self, name):
self.name = name
CLASS = A("me")
import b
go = B(6)
go.dostuff
文件b:
class B(object):
def __init__(self, number):
self.number = number
def dostuff(self):
from a import CLASS
print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number."
喉咙痛。
bar.py
print('going to import foo')
from foo import printx
foo.py
print('trying to import bar')
import bar
def printx():
print('x')
$ python bar.py
going to import foo
trying to import bar
going to import foo
Traceback (most recent call last):
File "bar.py", line 2, in <module>
from foo import printx
File "/home/suhail/Desktop/temp/circularimport/foo.py", line 2, in <module>
import bar
File "/home/suhail/Desktop/temp/circularimport/bar.py", line 2, in <module>
from foo import printx
ImportError: cannot import name 'printx' from partially initialized module 'foo' (most likely due to a circular import) (/home/suhail/Desktop/temp/circularimport/foo.py)
我完全同意pythoneer的回答。但是我偶然发现了一些代码,它们在循环导入时存在缺陷,并在尝试添加单元测试时引起了问题。因此,为了快速修补它而不改变一切,你可以通过动态导入来解决这个问题。
# Hack to import something without circular import issue
def load_module(name):
"""Load module using imp.find_module"""
names = name.split(".")
path = None
for name in names:
f, path, info = imp.find_module(name, path)
path = [path]
return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")
同样,这不是一个永久性的修复,但可以帮助那些希望在不修改太多代码的情况下修复导入错误的人。
干杯!
假设您正在运行一个名为request.py的测试python文件 在request.py中,您写入
import request
所以这也很可能是一个循环导入。
解决方案:
只需将测试文件更改为另一个名称,例如aaa.py,而不是request.py。
不要使用其他库已经使用过的名称。
这里有很多很棒的答案。虽然通常有快速解决问题的解决方案,其中一些比其他的更python化,但如果您有能力进行一些重构,另一种方法是分析代码的组织,并尝试删除循环依赖。例如,你可能会发现:
文件a.py
from b import B
class A:
@staticmethod
def save_result(result):
print('save the result')
@staticmethod
def do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
文件b.py
from a import A
class B:
@staticmethod
def do_something_b_ish(param):
A.save_result(B.use_param_like_b_would(param))
在这种情况下,只需要将一个静态方法移动到一个单独的文件中,例如c.py:
文件c.py
def save_result(result):
print('save the result')
将允许从A中删除save_result方法,从而允许从b中的A中删除A的导入:
重构文件a.py
from b import B
from c import save_result
class A:
@staticmethod
def do_something_a_ish(param):
save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
重构文件b.py
from c import save_result
class B:
@staticmethod
def do_something_b_ish(param):
save_result(B.use_param_like_b_would(param))
总之,如果你有一个工具(例如pylint或PyCharm),它报告的方法可以是静态的,只是在它们上抛出一个staticmethod装饰器可能不是静音警告的最好方法。尽管方法看起来与类相关,但最好将其分离出来,特别是如果您有几个密切相关的模块,它们可能需要相同的功能,并且您打算实践DRY原则。