在Python中,当两个模块试图相互导入时会发生什么?更一般地说,如果多个模块试图在一个循环中导入会发生什么?


另见我能做什么关于“ImportError:不能导入名称X”或“AttributeError:…”(很可能是由于循环导入)”?关于可能导致的常见问题,以及如何重写代码以避免此类导入的建议。参见为什么循环导入看起来在调用堆栈中更上一层,但随后在更下一层引发ImportError ?有关问题发生的原因和方式的技术细节。


当前回答

正如其他答案所描述的,这种模式在python中是可以接受的:

def dostuff(self):
     from foo import bar
     ...

这将避免在文件被其他模块导入时执行import语句。只有当存在逻辑循环依赖时,这才会失败。

大多数循环导入实际上不是逻辑循环导入,而是会引发ImportError错误,这是因为import()在调用时计算整个文件的顶级语句的方式。

如果你确实想要你的导入在顶部,这些ImportErrors几乎总是可以避免的:

考虑这个循环导入:

应用一个

# profiles/serializers.py

from images.serializers import SimplifiedImageSerializer

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)

应用程序B

# images/serializers.py

from profiles.serializers import SimplifiedProfileSerializer

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()

class ImageSerializer(SimplifiedImageSerializer):
    profile = SimplifiedProfileSerializer()

来自David Beazleys的精彩演讲:模块和包:生存和死亡!PyCon 2015, 1:54:00,这里是一个处理python循环导入的方法:

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

它尝试导入SimplifiedImageSerializer,如果ImportError被引发,因为它已经被导入,它将从importcache中拉出它。

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

其他回答

如果你导入foo(在bar.py内部)和导入bar(在foo.py内部),它会工作得很好。在实际运行任何东西时,两个模块都将完全加载,并将相互引用。

问题是当你做from foo import abc(在bar.py内)和from bar import xyz(在foo.py内)时。因为现在每个模块都需要另一个模块已经被导入(以便导入的名称存在),然后才能导入它。

我用下面的方法解决了这个问题,没有任何错误。 考虑两个文件a.py和b.py。

我把这个添加到a.py,它工作了。

if __name__ == "__main__":
        main ()

a.py:

import b
y = 2
def main():
    print ("a out")
    print (b.x)

if __name__ == "__main__":
    main ()

b.py:

import a
print ("b out")
x = 3 + a.y

得到的输出是

>>> b out 
>>> a out 
>>> 5

循环导入可能令人困惑,因为导入做了两件事:

它执行导入的模块代码 将导入的模块添加到导入模块全局符号表中

前者只执行一次,而后者则在每个import语句中执行。循环导入会在导入模块使用已导入的模块和部分执行的代码时产生这种情况。因此,它将看不到import语句后创建的对象。下面的代码示例演示了它。

循环进口并不是要不惜一切代价避免的终极祸害。在一些框架中,比如Flask,它们是很自然的,调整你的代码来消除它们并不会让代码变得更好。

main.py

print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __name__ == '__main__':
    print 'imports done'
    print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)

b.by

print "b in, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"

a.py

print 'a in, __name__ = {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"

Python main.py输出带有注释

import b
b in, __name__ = b    # b code execution started
b imports a
a in, __name__ = a    # a code execution started
a imports b           # b code execution is already in progress
b has x True
b has y False         # b defines y after a import,
a out
b out
a in globals() False  # import only adds a to main global symbol table 
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available

循环导入会终止,但在模块初始化期间,您需要注意不要使用循环导入的模块。

考虑以下文件:

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退出后发生。

这里有个例子让我震惊!

foo.py

import bar

class gX(object):
    g = 10

bar.py

from foo import gX

o = gX()

main.py

import foo
import bar

print "all done"

在命令行:$ python main.py

Traceback (most recent call last):
  File "m.py", line 1, in <module>
    import foo
  File "/home/xolve/foo.py", line 1, in <module>
    import bar
  File "/home/xolve/bar.py", line 1, in <module>
    from foo import gX
ImportError: cannot import name gX