递归导入语句

Recursive import statements

我对包中的递归导入语句有疑问。我看到了很多关于这个主题的帖子,但我找不到可行的解决方案。我复制了一个非常小的例子来隔离我的问题。

temp中的文件组织如下:

|-- __init__.py
|-- abstract.py
|-- a.py
|-- b.py

文件__init__.py包含

from .a import A
from .b import B

文件abstract.py包含

from abc import ABC, abstractmethod

class Abstract(ABC):

    @abstractmethod
    def __init__(self):
        pass

文件a.py包含

from .abstract import Abstract
from .b import B

class A(Abstract):

    def __init__(self, string):
        super().__init__()
        self.string = string

    def as_b(self):
        return B(int(self.string))

文件b.py包含

from .abstract import Abstract
from .a import A

class B(Abstract):

    def __init__(self, integer):
        super().__init__()
        self.integer = integer

    def as_a(self):
        return A(str(self.integer))

然后我创建了一个文件 foo.py 来测试包含

temp
from temp import A, B

an_a = A("2")
a_b = B(4)

an_a_as_b = A.as_b()
a_b_as_a = B.as_a()

在 运行 时,我收到错误 ImportError: cannot import name 'A'。我的理解是,这是由于递归导入语句(class A 导入 class B,反之亦然)。

temp 包中实现 classes AB 的最佳 pythonic 方法是什么?

除了合并文件之外还有几个选项,您曾说过您的工作场所惯例不鼓励这样做。 (下面的选项适用于 Python 3 相对于 中链接的答案。我认为在这里包含一个答案是有意义的,因为这个问题不直接与相对进口有关。)

将循环导入移动到其文件的末尾

文件a.py变为:

from .abstract import Abstract

class A(Abstract):
    # ...

from .b import B

b.py的变化方式相同。这是一个简单的更改,但缺点是您的导入是分散的。有人可能会 "clean up" 通过将导入移动到顶部来 运行 您的代码,并且 运行 陷入与您相同的困惑。 (您可以发表评论,但人们经常会错过评论。)

导入模块而不是 类

您还可以让每个模块导入另一个模块,而不是从中导入。 a.py 变为:

from .abstract import Abstract
from . import b

class A(Abstract):
    # ...
    def as_b(self):
        return b.B(int(self.string)) # note the change to use b.B

当然 b.py 也有类似的变化。这让我觉得这是最干净的解决方案——我强烈希望尽可能将导入放在一起。 (我想不出这种方法有什么缺点,但如果有人知道任何缺点,请发表评论,我会更新。)

将导入移动到使用它们的地方

如果您只是在一个地方从 a.py 引用 B,您可以将导入移动到需要它的函数中:

from .abstract import Abstract

class A(Abstract):
    # ...

    def as_b(self):
        from .b import B
        return B(int(self.string))

b.py 也是如此。与第一个选项相比,这有一个优势,那就是更容易看出为什么导入不在顶部,而且我觉得它不太可能导致混淆或错误。但是,它可以掩盖导入错误,因此我认为这不是理想的选择。它也会稍微减慢 as_aas_b 的第一次调用,因为导入将在第一次调用每个函数时发生,并且需要非零时间。