ImportError: cannot import name between two files importing the same

ImportError: cannot import name between two files importing the same

我有这个代码

主模块

from src.comp.mypackage.wait import Wait
from src.comp.mypackage.men import Men, MenItem

""" Code and stuff using Men and MenItem """

if __name__ == '__main__':
    MenuTestDrive.main()

男性

from abc import ABCMeta, abstractmethod
from src.comp.mypackage.util import NullUtil, CompUtil

实用程序

from src.comp.mypackage.stack import Stack
from src.comp.mypackage.men import Men

""" Code and stuff using Men and MenItem """

当 运行 mainModule 出现此错误时:

Traceback (most recent call last):
  File "/home/user/PycharmProjects/pythonProj/pythonDesignPatterns/src/comp/mypackage/mainModule.py", line 2, in <module>
    from pythonDesignPatterns.src.comp.mypackage.men import Men, MenItem
  File "/home/user/PycharmProjects/pythonProj/pythonDesignPatterns/src/comp/mypackage/men.py", line 2, in <module>
    from pythonDesignPatterns.src.comp.mypackage.iterator import NullUtil, CompUtil
  File "/home/user/PycharmProjects/pythonProj/pythonDesignPatterns/src/comp/mypackage/util.py", line 2, in <module>
    from pythonDesignPatterns.src.comp.mypackage.men import Men

ImportError: cannot import name 'Men'

我正在使用 pyCharm,但命令行的错误是相同的。

我可以提供更多代码,但我不认为使用 类 有什么特别之处,只会分散注意力。

我应该去哪里寻找故障?

TL;DR : python 不允许循环导入,所以你不能让模块 men 从模块 [=11= 导入] 如果模块 util 从模块 men 导入。

更长的答案:

你必须明白 Python importclassdef 等实际上是可执行语句,所有(或几乎)发生在运行时。当在给定进程中第一次导入模块时,所有顶级语句按顺序执行,创建一个 module 实例对象,并将所有顶级名称作为属性(注意 classdefimport 都是绑定名称)并插入到 sys.modules 缓存字典中,因此下一次导入同一模块将直接从缓存中检索它。

在您的情况下,首次导入时,men 模块会尝试导入 util 模块,该模块不在 sys.modules 中,因此 Python 运行时定位util.py(或 .pyc)文件并执行它。然后它到达 from src.comp.mypackage.men import Men。此时,men.py 尚未完全执行,因此没有 Men 属性。

规范的解决方案是要么将循环依赖提取到第三个模块中,要么将两个模块合并为一个模块,具体取决于哪个模块对您的具体情况有意义(一如既往,目标是让模块具有低耦合和高内聚)。 FWIW,循环依赖被认为是糟糕的设计,无论语言如何,即使语言支持它们。

有时(最常见的是在将某些特定结构强加给您的代码和特定导入顺序的复杂框架中),您最终可能会得到更加复杂的循环依赖链(例如 A.funcX 取决于B.y,B 依赖于 C,而 C 又依赖于 D,而 D 又依赖于 E,而 E 最终又依赖于 A.funcZ) and/or 很难以一种有意义的方式彻底重构。作为最后的手段,您仍然可以在函数内延迟一些导入语句(在上面它会在 A.funcX 内)。这仍然被认为是一种不好的做法,实际上应该只作为最后的手段使用。

附带说明: 从您的包命名方案中我可以闻到强烈的 Java 影响。 Python 不是 Java ! 并不是说​​ Java 有什么问题,只是 these are two wildly different languages, with wildly different designs, idioms and philosopies

试图在 Python 中强制适应 Java 习语和习惯充其量是一种痛苦和挫折的体验(到过这里,做过那个……),所以我的建议是:忘掉大部分你在 Java 中学到的东西,开始学习 Python(不仅仅是语法——语法实际上只是语言的一部分,不一定是最重要的语言)。在 Python 中,我们更喜欢扁平而不是嵌套,并且不要尝试每个 class 有一个模块,你可以在一个模块中有一个完整的(微)框架,如果没有实际理由这样做也没关系将其拆分为子模块。