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 abc
和from 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'
这是因为模块是在导入时执行的,在访问bx
, x = 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
使用,但是你还有其他的东西,要么是def
, class
,要么是文件b
variables。 你可以做的是,在文件a
的底部,在调用文件b
需要的文件b
的函数或类之后,但是在从文件b
调用文件b
所需的函数或类之前,先说import b
然后,这里是关键部分 ,在文件b
中的所有定义或类中,需要文件a
(我们称之为CLASS
)的def
或class
,你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."
瞧。