Answers:
去年在comp.lang.python上对此进行了非常好的讨论。它相当彻底地回答了您的问题。
导入确实非常简单。只要记住以下几点:
'import'和'from xxx import yyy'是可执行语句。它们在运行的程序到达该行时执行。
如果模块不在sys.modules中,则导入将在sys.modules中创建新的模块条目,然后在模块中执行代码。在执行完成之前,它不会将控制权返回给调用模块。
如果sys.modules中确实存在某个模块,则无论导入是否完成执行,导入都会简单地返回该模块。这就是循环导入可能返回部分为空的模块的原因。
最后,执行脚本在名为__main__的模块中运行,以其自己的名称导入脚本将创建一个与__main__无关的新模块。
放在一起,导入模块时就不会感到惊讶。
如果在import foo
内部bar
和import bar
内部进行操作foo
,它将正常工作。到实际运行任何东西时,两个模块都将完全加载,并且将相互引用。
问题是当您改为from foo import abc
和时from bar import xyz
。因为现在每个模块都需要先导入另一个模块(这样才能导入我们要导入的名称),然后才能导入它。
from foo import *
和from bar import *
也将正常工作。
from x import y
,但仍然收到循环导入错误
import
执行该语句时可用的名称。因此它不会出错,但是您可能无法获得所有期望的变量。
from foo import *
和from bar import *
,foo
则在中执行的所有操作都处于的初始化阶段bar
,并且bar
尚未定义中的实际功能……
循环导入会终止,但是您需要注意不要在模块初始化期间使用循环导入的模块。
考虑以下文件:
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时(在第二个中a in
),Python解释器不会b
再次导入,因为它已经存在于模块dict中。
如果您尝试在模块初始化期间b.x
从中进行访问a
,则会显示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 out
。
__name__
而不是,此答案将大有裨益'a'
。一开始,我完全困惑为什么文件要执行两次。
正如其他答案所描述的那样,这种模式在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)
# 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.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
import bar
在foo.py
到最后
bar
和foo
两者都必须使用gX
的“干净”的解决方案是把gX
另一个模块,并同时拥有foo
和bar
进口该模块。(就没有隐藏的语义依赖性而言,这是最干净的。)
bar
找不到gX
。循环导入本身就可以,但是只是gX
在导入时未定义。
模块a.py:
import b
print("This is from module a")
模块b.py
import a
print("This is from module b")
运行“模块a”将输出:
>>>
'This is from module a'
'This is from module b'
'This is from module a'
>>>
它输出这3行,而由于循环导入而应该输出无穷大。下面列出了在运行“模块a”时逐行发生的情况:
import b
。因此它将访问模块bimport a
。因此它将访问模块aimport b
但是请注意,此行将不再执行,因为python中的每个文件仅执行一次导入行,因此无论在何时何地执行都无关紧要。因此它将传递到下一行并打印"This is from module a"
。"This is from module b"
"This is from module a"
,程序将完成。我完全同意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")
同样,这不是永久性的修复,但是可以帮助想要修复导入错误而无需更改太多代码的人。
干杯!
这里有很多很好的答案。尽管通常可以快速解决问题,但有些解决方案比其他解决方案更具有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')
将允许save_result
从A中删除方法,从而允许从b中的a中删除A的导入:
重构文件a.py
from b import B
from c import save_result
class A:
@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 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原理时。
循环导入可能会造成混淆,因为导入有两件事:
前者仅执行一次,而后者在每个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"
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
好的,我想我有一个很酷的解决方案。假设您有file a
和file b
。你有一个def
或一个class
文件b
要在模块来使用a
,但你有别的东西,无论是一个def
,class
或者从文件变量a
您在文件中定义或类需要b
。您可以做的是,在文件底部a
,在调用文件a
中所需的文件中b
的函数或类之后,但是在从文件b
中调用所需的文件中调用函数或类之前a
,说import b
然后,这是关键部分,在文件b
中所有需要the def
or class
from file 的定义或类中a
(叫它CLASS
),你说from a import CLASS
之所以可行,是因为您可以导入文件b
而无需Python在file中执行任何import语句b
,因此可以避免任何循环导入。
例如:
class A(object):
def __init__(self, name):
self.name = name
CLASS = A("me")
import b
go = B(6)
go.dostuff
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."
瞧
from a import CLASS
实际上并不会跳过执行a.py中的所有代码。这就是实际发生的情况:(1)a.py中的所有代码都作为特殊模块“ __main__”运行。(2)在处import b
运行b.py中的顶级代码(定义类B),然后控制权返回到“ __main__”。(3)“ __main__”最终将控制权交给go.dostuff()
。(4)当dostuff()出现时import a
,它将再次运行a.py中的所有代码,这次作为模块“ a”运行;然后从新模块“ a”导入CLASS对象。因此,实际上,如果您import a
在b.py中的任何地方使用它,也将同样有效。