如何避免Python中的循环导入?

9

一种方法是使用import x,而不使用“from”关键字。这样你就可以在任何地方引用它们的名称空间。

还有其他方法吗?例如,在C ++中做ifnotdef __b__ def __b__类型的事情吗?


2
不要出现循环依赖,请在 y 中组合 x 的依赖和 y 在 x 中的依赖来创建 z。 - Esailija
此帖子为重复内容:https://dev59.com/hXRB5IYBdhLWcg3wAjbR - alinsoar
2
@alinsoar 这不是完全重复的问题 - 那个问题问的是当您有循环导入时会发生什么; 而这个问题则询问避免它们的技巧。 - Edward Loper
这是关于两个相互导入的模块,即循环的问题。我没有看解决方案。问题仍然存在。 - alinsoar
你是想要执行 from foo import bar 还是 from foo import *?如果是后者,那么答案是不要这样做。除了一些特殊情况(在快速脚本或交互式会话之外,循环导入不会出现),你才需要这样做,而且我无法想到任何情况下 from foo import * 和相互依赖都是有意义的。 - abarnert
另外,如果你想要实现的目标是能够键入f而不是x.f,那么为什么呢?是因为x实际上是一个40个字符长的嵌套模块名称,还是因为你想让Python感觉更像是另一种语言,或者出于某些真正的原因(例如,你想尝试from json import loads并在失败时定义一个本地的loads)? - abarnert
3个回答

10

将任何相互依赖的模块合并为一个模块。然后引入额外的模块以恢复旧名称。

例如,

# a.py
from b import B

class A: whatever

# b.py
from a import A

class B: whatever

变成

# common.py
class A: whatever
class B: whatever

# a.py
from common import A

# b.py
from common import B

31
这会迫使我们把所有东西都声明在common.py而不是a.pyb.py中。在我看来,这不是一个好的解决方案。 - Nick

5
循环导入是一种“代码味道”,通常(但不总是)表明需要进行某些重构。例如,如果A.x使用B.y,而B.y使用A.z,则可以考虑将A.z移动到其自己的模块中。
如果您确实需要循环导入,则通常建议导入模块并使用完全限定名称引用对象(即,import A并使用A.x而不是from A import x)。

3
如果有人正在进行显式类型检查,那该怎么办?我现在正在尝试这样做,以便提供非常详细的错误消息,而不是一些顾客无法理解的混淆的回溯信息。 - Nick

2
如果你试图使用from A import *,答案非常简单:不要这样做。通常应该使用import A并引用限定名称。
对于快速粗略的脚本和交互式会话,这是完全合理的事情——但在这种情况下,你不会遇到循环导入。
有些情况下,在真正的代码中使用import *是有意义的。例如,如果你想隐藏一个复杂的模块结构,或者你动态生成的模块结构或版本之间经常发生变化,或者你正在封装某个嵌套太深的其他人的包,import *可能从“包装器模块”或顶级包模块的角度讲是合理的。但在这种情况下,你导入的任何内容都不会导入你自己。
事实上,我很难想象出任何情况下需要使用import *且循环依赖甚至是可能存在的。
如果你正在执行from A import foo,有办法解决这个问题(例如,import A然后foo = A.foo)。但你可能不想这样做。再次考虑一下是否真的需要将foo带入你的命名空间——限定名称是一种特性,而不是需要解决的问题。
如果你只是为了方便实现函数而执行from A import foo,因为A实际上是long_package_name.really_long_module_name,并且由于所有这些调用long_package_name.really_long_module_name.long_class_name.class_method_that_puts_me_over_80_characters而导致你的代码难以阅读,请记住,你总是可以使用import long_package_name.really_long_module_name as P,然后对你的限定调用使用P
(另外,请记住,在任何出于实现方便而执行的from中,你可能希望确保指定一个__all__,以确保导入的名称不会出现在你的命名空间中,如果有人从交互式会话中对你进行import *。)
此外,正如其他人指出的那样,大多数但不是全部循环依赖情况都是设计不良的症状,通过合理地重构你的模块来解决它。在真正需要将名称带入你的命名空间,并且循环的一组模块实际上是最好的设计的罕见情况下,一些人工重构仍然比foo = A.foo更好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接