我知道python中循环导入的问题已经出现过很多次了,我已经阅读过这些讨论了。在这些讨论中反复提出的评论是,循环导入是设计错误的标志,应重新组织代码以避免循环导入。
有人可以告诉我如何在这种情况下避免循环导入吗?:我有两个类,我希望每个类都有一个构造函数(方法),它接受另一个类的实例并返回该类的实例。
更具体地说,一个类是可变的,一个是不可变的。不可变类是必需的 用于散列,比较等。可变类也需要做事。这类似于sets和frozensets或者列表和元组。
我可以将两个类定义放在同一个模块中。还有其他建议吗?
玩具示例将是A类,其具有属性是列表,而类B具有属性是元组。然后类A有一个方法,它接受类B的实例并返回类A的实例(通过将元组转换为列表),类似地,类B有一个方法,它接受类A的实例并返回类B的实例(通过将列表转换为元组)。
答案 0 :(得分:139)
考虑以下示例python包,其中a.py
和b.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
不幸的是,当你有循环依赖时,只有第一个和第四个选项实际上有效(其余的都是ImportError
或AttributeError
)。通常,您不应该使用第4种语法,因为它仅适用于python2并且存在与其他第三方模块冲突的风险。实际上,只有第一种语法才能保证正常工作。但是,在处理循环依赖时,您仍有几个选项。
编辑:
ImportError
和AttributeError
问题仅发生在 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
这也有效,但与第一种方法有同样的问题,其中所有包和子模块调用都得到超长。它还有两个主要缺陷 - 它强制导入所有子模块,即使你只使用一个或两个,你仍然无法查看任何子模块并快速查看他们在顶部的依赖关系,你必须筛选功能。
在__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()
所以这些是你的选择(他们都有点吮吸IMO)。坦率地说,这似乎是python导入机制中的一个明显的错误,但这只是我的观点。
答案 1 :(得分:75)
仅导入模块,不要从模块导入:
考虑a.py
:
import b
class A:
def bar(self):
return b.B()
和b.py
:
import a
class B:
def bar(self):
return a.A()
这完全没问题。
答案 2 :(得分:5)
我们将绝对导入和函数组合在一起,以便更好地阅读和缩短访问字符串。
主/副/ 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__)