Python中的循环(或循环)导入

如果两个模块相互导入会发生什么?

为了概括这个问题,Python中的循环导入呢?

去年在comp.lang.python上有一个很好的讨论。 它相当彻底地回答你的问题。

导入非常简单。 请记住以下几点:

'import'和'from xxx import yyy'是可执行语句。 它们在正在运行的程序到达该行时执行。

如果一个模块不在sys.modules中,则导入将在sys.modules中创build新的模块条目,然后执行该模块中的代码。 在执行完成之前,它不会将控制权返回给调用模块。

如果一个模块存在于sys.modules中,那么一个导入只是返回该模块,不pipe它是否已经完成执行。 这就是为什么循环import可能会返回看似部分空的模块。

最后,执行脚本运行在名为__main__的模块中,以其自己的名字导入脚本将创build一个与__main__无关的新模块。

把这件事情放在一起,导入模块时你不应该感到惊讶。

如果你在import foo里面import foo并且在import foo里面import bar ,它会正常工作。 到实际运行的时候,两个模块都会被完全加载,并且会相互引用。

问题是,而不是你from foo import abcfrom bar import xyz 。 因为现在每个模块都要求其他模块已经被导入(所以我们导入的名字已经存在)才能被导入。

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

考虑以下文件:

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 

在第二次inputb.py(在第二个a in )时,Python解释器不会再次inputb ,因为它已经存在于模块字典中。

如果您尝试在模块初始化期间访问bx ,您将会得到一个AttributeError

a.py添加到a.py

 print bx 

那么,输出是:

 $ 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 bx AttributeError: 'module' object has no attribute 'x' 

这是因为模块是在导入时执行的,在访问bxx = 3还没有执行,只会在b out之后发生。

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

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

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

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

这些ImportErrors几乎总是可以避免的,如果你积极地想要你的导入

考虑这个通告:

应用程序A

 # 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出色的演讲Module and Packages:Live and Let Die! – PyCon 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.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 

我完全同意pythoneer的答案。 但是我偶然发现了一些与循环导入有关的代码,并在尝试添加unit testing时引发了一些问题。 所以要快速修补它而不改变一切,你可以通过dynamic导入来解决问题。

 # 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") 

再次,这不是一个永久性的修复,但可以帮助某人想要修改一个导入错误,而不会改变太多的代码。

干杯!

好吧,我想我有一个非常酷的解决scheme。 假设你有文件a和文件b 。 你在文件b中有一个def或者一个class ,你想在模块a使用,但是你还有其他的东西,要么是defclass ,要么是文件bvariables。 你可以做的是,在文件a的底部,在调用文件b需要的文件b的函数或类之后,但是在从文件b调用文件b所需的函数或类之前,先说import b然后,这里是关键部分 ,在文件b中的所有定义或类中,需要文件a (我们称之为CLASS )的defclass ,你from a import CLASS

这是可行的,因为你可以导入文件b而不用Python执行文件b任何导入语句,因此你避开了任何循环导入。

例如:

文件a:

 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." 

瞧。