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


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


当前回答

我用下面的方法解决了这个问题,没有任何错误。 考虑两个文件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

其他回答

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

喉咙痛。

这里有很多很棒的答案。虽然通常有快速解决问题的解决方案,其中一些比其他的更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原则。

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)

假设您正在运行一个名为request.py的测试python文件 在request.py中,您写入

import request

所以这也很可能是一个循环导入。

解决方案:

只需将测试文件更改为另一个名称,例如aaa.py,而不是request.py。

不要使用其他库已经使用过的名称。

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

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

前者只执行一次,而后者则在每个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