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

时间:2011-09-07 15:49:54

标签: python import circular-dependency

我知道python中循环导入的问题已经出现过很多次了,我已经阅读过这些讨论了。在这些讨论中反复提出的评论是,循环导入是设计错误的标志,应重新组织代码以避免循环导入。

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

更具体地说,一个类是可变的,一个是不可变的。不可变类是必需的 用于散列,比较等。可变类也需要做事。这类似于sets和frozensets或者列表和元组。

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

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

3 个答案:

答案 0 :(得分:139)

考虑以下示例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)。通常,您不应该使用第4种语法,因为它仅适用于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

将所有导入放入中央模块

这也有效,但与第一种方法有同样的问题,其中所有包和子模块调用都得到超长。它还有两个主要缺陷 - 它强制导入所有子模块,即使你只使用一个或两个,你仍然无法查看任何子模块并快速查看他们在顶部的依赖关系,你必须筛选功能。

__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__)