我有一个测试环境,试图了解如何使用import x
语句导入模块而不是使用from x import y
来避免python循环依赖:
test/
__init__.py
testing.py
a/
__init__.py
m_a.py
b/
__init__.py
m_b.py
这些文件包含以下内容:
testing.py:
from a.m_a import A
m_a.py:
import b.m_b
print b.m_b
class A:
pass
m_b.py:
import a.m_a
print a.m_a
class B:
pass
有一种我无法理解的情况:
如果我从模块m_a.py
和m_b.py
或仅从m_b.py
删除打印语句,则此功能正常,但如果m_b.py
处有打印,则以下内容抛出错误:
File "testing.py", line 1, in <module>
from a.m_a import A
File "/home/enric/test/a/m_a.py", line 1, in <module>
import b.m_b
File "/home/enric/test/b/m_b.py", line 3, in <module>
print a.m_a
AttributeError: 'module' object has no attribute 'm_a'
你有什么想法吗?
答案 0 :(得分:1)
只有删除了打印语句才能“正常”,因为你实际上并没有做任何依赖于导入的事情。它仍然是一个破碎的循环导入。
在调试器中运行它,或者在每行之后添加print
语句,您将看到会发生什么:
from a.m_a import A
import b.m_b
import a.m_a
print a.m_a
在模块完成导入之前,显然正在尝试访问a.m_a
。 (事实上,您可以在回溯中看到堆栈中剩余的a.m_a
。)
如果您此时转出sys.modules
,您会找到两个名为a
和a.m_a
的部分模块,但如果您dir(a)
,则没有m_a
还有。
据我所知,在m_a
完成评估之前,a
未添加到m_a.py
的事实未在Python 2.7文档中的任何位置记录。 (3.x有一个更完整的导入过程规范 - 但它也是一个非常不同的导入过程。)因此,你不能依赖这个失败或成功;任何一个都是完全合法的实施。 (但它至少在CPython和PyPy中失败了......)
更一般地说,使用import foo
代替from foo import bar
并不会神奇地解决所有循环导入问题。它只解决了一类特殊的循环导入问题(或者更确切地说,使得该类没有实际意义)。 (我意识到the FAQ中有一些误导性的文字。)
有各种各样的技巧可以解决循环导入问题,同时仍然允许您拥有循环顶级依赖项。但实际上,摆脱循环顶级依赖关系几乎总是更简单。
在这个玩具案例中,a.m_a
根本没有理由完全依赖b.m_b
。如果你需要打印a.m_a
的一些,有更好的方法来获得它而不是完全独立的包!
在现实代码中,m_a
中可能存在m_b
需要的一些内容,反之亦然。但通常情况下,您可以将其分为两个级别:m_a
中需要m_b
的内容,以及m_a
中需要m_b
内容的内容。所以,只需将其拆分为两个模块。这与尝试备份和import main
的一堆模块的常见修复方法完全相同:将utils
分开main
。
如果m_b
需要m_a
确实存在需要m_b
的内容,该怎么办?那么,在这种情况下,您可能必须插入一个间接级别。例如,也许您可以将事物从m_b
传递到函数/构造函数/来自m_a
的任何内容,因此它可以将其作为本地参数值而不是全局访问。 (没有更具体的问题,很难更具体。)
如果最糟糕的情况发生,并且您无法通过间接删除导入,则必须将导入移出。这可能再次意味着在函数调用等内部进行导入(如紧接在段落之后的常见问题解答中所解释的那样),或者只是在导入之上移动一些代码,或者各种其他可能性。但是考虑一下这些最后的解决方案,这些解决方案不能干净利落地设计,而不是你设计的路线图。