How to avoid circular imports in Python?
我知道在python中循环导入的问题已经出现过很多次了,我已经阅读了这些讨论。在这些讨论中反复作出的评论是,循环导入是设计错误的标志,应该重新组织代码以避免循环导入。
有人能告诉我在这种情况下如何避免循环导入吗?:我有两个类,我希望每个类都有一个构造函数(方法),它获取另一个类的实例并返回该类的实例。
更具体地说,一个类是可变的,一个是不可变的。需要不可变类用于散列、比较等。可变类也需要做一些事情。这与集合和冻结集或列表和元组类似。
我可以将两个类定义放在同一个模块中。还有其他建议吗?
一个玩具的例子是类A,它有一个属性是列表,类B,它有一个属性是元组。然后,类A有一个方法,该方法获取类B的实例并返回类A的实例(通过将元组转换为列表),同样,类B也有一个方法,该方法获取类A的实例并返回类B的实例(通过将列表转换为元组)。
考虑下面的示例python包,其中
1 2 3 4 | /package __init__.py a.py b.py |
在python中导入模块有几种方法
1 2 3 4 5 | 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 |
不幸的是,只有第1和第4个选项在具有循环依赖关系时才有效(其余选项都会提升
EDIT: The
ImportError andAttributeError issues only occur in
python 2. In python 3 the import machinery has been rewritten and all
of these import statements (with the exception of 4) will work, even with
circular dependencies.
使用绝对导入
只需使用上面的第一个导入语法。这种方法的缺点是,对于大型包,导入名称可能会变得过长。
在
1 | import package.b |
在
1 | import package.a |
将导入推迟到稍后
我在很多软件包中都看到过这种方法,但我仍然觉得它很糟糕,我不喜欢看不到模块的顶部并看到它的所有依赖项,我还必须搜索所有的函数。
在
1 2 | def func(): from package import b |
在
1 2 | def func(): from package import a |
将所有导入内容放入中央模块
这也可以,但与第一个方法有相同的问题,在这个方法中,所有的包和子模块调用都是超长的。它还存在两个主要缺陷——它强制导入所有子模块,即使您只使用一个或两个,并且您仍然无法查看任何子模块,并且很快在顶部看到它们的依赖关系,您必须筛选函数。
在
1 2 | from . import a from . import b |
在
1 2 3 4 | import package def func(): package.b.some_object() |
在
1 2 3 4 | import package def func(): package.a.some_object() |
所以这些是你的选择(在我看来都很糟糕)。坦率地说,这似乎是python导入机器中的一个明显缺陷,但这只是我的观点。
只导入模块,不从模块导入:
以
1 2 3 4 5 | import b class A: def bar(self): return b.B() |
和
1 2 3 4 5 | import a class B: def bar(self): return a.A() |
这个很好用。
我们结合了绝对导入和函数,以便更好地读取和缩短访问字符串。
- 优点:与纯绝对导入相比,访问字符串更短
- 缺点:额外的函数调用会增加一些开销
主/子/ A/Py
1 2 3 4 5 6 | import main.sub.b b_mod = lambda: main.sub.b class A(): def __init__(self): print('in class"A":', b_mod().B.__name__) |
主/副/BY
1 2 3 4 5 6 | import main.sub.a a_mod = lambda: main.sub.a class B(): def __init__(self): print('in class"B":', a_mod().A.__name__) |