递归导入语句
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 A
和 B
的最佳 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_a
和 as_b
的第一次调用,因为导入将在第一次调用每个函数时发生,并且需要非零时间。
我对包中的递归导入语句有疑问。我看到了很多关于这个主题的帖子,但我找不到可行的解决方案。我复制了一个非常小的例子来隔离我的问题。
包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 A
和 B
的最佳 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_a
和 as_b
的第一次调用,因为导入将在第一次调用每个函数时发生,并且需要非零时间。