网上有很多这样的python
循环导入问题。我选择为这个线程做贡献是因为查询中有 Ray Hettinger 的评论,它使循环导入的用例合法化,但推荐了一个我认为不是特别好的做法的解决方案 - 将导入移动到一个方法。
除了 Hettinger 的权威,对共同反对的三个免责声明是必要的:
- 我从来没有用 Java 编程过。我不想做Java风格。
- 重构并不总是有用或有效的。逻辑 API 有时会规定一种结构,使递归导入引用不可避免。请记住,代码是为用户而存在的,而不是为程序员而存在的。
- 组合相当大的模块可能会导致可读性和可维护性问题,这些问题可能比一两次递归导入要糟糕得多。
此外,我相信可维护性和可读性要求导入在文件顶部分组,每个需要的名称只出现一次,并且from module import name
样式更可取(可能除了具有许多功能的非常短的模块名称,例如gtk
),如它避免了重复的语言混乱并使依赖关系明确。
有了这个,我将假设我自己的用例的简化版本将我带到这里,并提供我的解决方案。
我有两个模块,每个模块都定义了许多类。 surface
定义几何表面,如平面、球体、双曲线等。 path
定义平面几何图形,如直线、圆、双曲线等。从逻辑上讲,这些是不同的类别,从 API 要求的角度来看,重构不是一种选择。然而,这两个类别是亲密的。
一个有用的操作是相交两个曲面,例如,两个平面的相交是一条线,或者一个平面和一个球体的相交是一个圆。
例如,如果surface.py
您执行实现交集操作的返回值所需的直接导入:
from path import Line
你得到:
Traceback (most recent call last):
File "surface.py", line 62, in <module>
from path import Line
File ".../path.py", line 25, in <module>
from surface import Plane
File ".../surface.py", line 62, in <module>
from path import Line
ImportError: cannot import name Line
在几何上,平面用于定义路径,毕竟,它们可以在三个(或更多)维度上任意定向。回溯会告诉您正在发生的事情和解决方案。
只需将 import 语句替换为surface.py
:
try: from path import Line
except ImportError: pass # skip circular import second pass
追溯中的操作序列仍在发生。只是第二次通过,我们忽略了导入失败。这无关紧要,因为Line
不在模块级别使用。因此,必要的命名空间surface
被加载到path
. 因此可以完成对 的命名空间解析path
,允许将其加载到surface
中,从而完成与 的第一次相遇from path import Line
。因此,名称空间解析surface
可以继续并完成,继续进行任何其他可能需要的操作。
这是一个简单明了的成语。try: ... except ...
语法清晰简洁地记录了循环导入问题,简化了未来可能需要的任何维护。每当重构确实是一个坏主意时使用它。