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

我知道Python的循环导入问题已经出现了很多次,我已经阅读了这些讨论。 在这些讨论中反复提出的意见是,循环import是一个糟糕的devise的标志,应重新组织代码,以避免循环导入。

有人可以告诉我如何在这种情况下避免循环导入吗?我有两个类,我希望每个类都有一个构造函数(方法),它需要另一个类的实例并返回类的一个实例。

更具体地说,一个类是可变的,一个是不可变的。 哈希,比较等需要不可变类。 可变类也需要做的事情。 这与sets和frozensets或列表和元组类似。

我可以把两个类定义放在同一个模块中。 还有其他build议吗?

一个玩具的例子是A类,它有一个属性是一个列表,而B类有一个属性是一个元组。 然后,类A有一个方法,它接受一个类B的实例,并返回一个类A的实例(通过将元组转换为一个列表),同样,类B有一个方法,它接受一个类A的实例并返回一个类B的实例(通过将列表转换为元组)。

只导入模块,不要从模块导入:

考虑a.py

 import b class A: def bar(self): return bB() 

b.py

 import a class B: def bar(self): return aA() 

这工作得很好。

考虑下面的例子python包,其中a.pyb.py相互依赖:

 /package __init__.py a.py b.py 

有几种方法可以在python中导入模块

 import package.a # Absolute import import package.a as a_mod # Absolute import bound to different name from package import a # Alternate absolute import import a # Implicit relative import (deprecated, py2 only) from . import a # Explicit relative import 

不幸的是,当你有循环依赖时,只有第一个和第四个选项实际上工作(其余的都会引发ImportErrorAttributeError )。 一般来说,你不应该使用第四种语法,因为它只能在python2中运行,并且有可能与其他第三方模块发生冲突。 所以真的,只有第一个语法是保证工作。 但是,在处理循环依赖时,您仍然有几个选项。

编辑: ImportErrorAttributeError问题只发生在python 2中。在python 3中,导入机制已被重写,所有这些导入语句(除了4)都可以工作,即使是循环依赖。

使用绝对导入

只需使用上面的第一个导入语法。 这种方法的缺点是导入名称对于大包来说可能会超长

a.py

 import package.b 

b.py

 import package.a 

推迟导入,直到更晚

我已经看到这种方法在很多软件包中使用,但是对我来说仍然感觉很不舒服,而且我不喜欢看模块的顶部并查看它的所有依赖关系,我必须去查找所有的函数以及。

a.py

 def func(): from package import b 

b.py

 def func(): from package import a 

把所有的import放在一个中央模块

这也适用,但是与第一种方法相同的问题是,所有的包和子模块调用都超长 。 它也有两个主要的缺陷 – 即使你只使用一个或两个子模块 ,也会强制所有的子模块被导入,而你仍然无法查看任何子模块,并很快看到它们的依赖关系,必须通过function筛选。

__init__.py

 from . import a from . import b 

a.py

 import package def func(): package.b.some_object() 

b.py

 import package def func(): package.a.some_object() 

所以这些是你的select(他们都有吸引IMO)。 坦率地说,这似乎是python引进机器中的一个明显的缺陷,但这只是我的看法。

我们做绝对导入和function的组合,以获得更好的阅读和更短的访问string。

  • 优点:与纯绝对import相比,访问string更短
  • 缺点:由于额外的函数调用会带来更多的开销

主/副/ a.py

 import main.sub.b b_mod = lambda: main.sub.b class A(): def __init__(self): print('in class "A":', b_mod().B.__name__) 

主/副/ b.py

 import main.sub.a a_mod = lambda: main.sub.a class B(): def __init__(self): print('in class "B":', a_mod().A.__name__)