小能豆

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

javascript

我知道 Python 中的循环导入问题之前已经出现过很多次,我也读过这些讨论。这些讨论中反复出现的评论是,循环导入是糟糕设计的标志,应该重新组织代码以避免循环导入。

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

更具体地说,一个类是可变的,另一个是不可变的。不可变类用于哈希、比较等。可变类也用于执行操作。这类似于集合和冻结集或列表和元组。

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

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


阅读 35

收藏
2024-08-26

共1个答案

小能豆

要避免 Python 中的循环导入问题,尤其是在两个类相互依赖时,可以使用以下几种策略来重构和设计代码。下面是几个可行的方法,适合你的情况:

1. 将两个类放在同一个模块中

如果两个类密切相关,将它们放在同一个模块中是一个简单有效的解决方案。这种方式避免了导入循环问题,因为两个类定义在同一个文件中。

# mymodule.py

class A:
    def __init__(self, items):
        self.items = items

    def to_list(self):
        return self.items

    def convert_from_b(self, b_instance):
        return A(list(b_instance.items))

class B:
    def __init__(self, items):
        self.items = items

    def to_tuple(self):
        return tuple(self.items)

    def convert_from_a(self, a_instance):
        return B(tuple(a_instance.items))

2. 在方法中使用局部导入

如果你希望将两个类保留在不同的模块中,可以在需要时进行局部导入,以避免循环导入问题。

文件: a.py

class A:
    def __init__(self, items):
        self.items = items

    def convert_from_b(self, b_instance):
        from b import B  # 局部导入
        return A(list(b_instance.items))

文件: b.py

class B:
    def __init__(self, items):
        self.items = items

    def convert_from_a(self, a_instance):
        from a import A  # 局部导入
        return B(tuple(a_instance.items))

3. 重构公共逻辑到第三个模块

将公共逻辑或共享功能提取到一个独立的模块中,这样可以打破类之间的直接依赖关系,避免循环导入问题。

文件: common.py

def list_to_tuple(lst):
    return tuple(lst)

def tuple_to_list(tpl):
    return list(tpl)

文件: a.py

from common import tuple_to_list

class A:
    def __init__(self, items):
        self.items = items

    def convert_from_b(self, b_instance):
        return A(tuple_to_list(b_instance.items))

文件: b.py

from common import list_to_tuple

class B:
    def __init__(self, items):
        self.items = items

    def convert_from_a(self, a_instance):
        return B(list_to_tuple(a_instance.items))

4. 使用类型注解和延迟导入

如果你使用的是 Python 3.7 及以上版本,可以利用类型注解和 TYPE_CHECKING 进行延迟导入。这可以帮助在类型检查时解决依赖问题,而在实际运行时避免导入循环。

文件: a.py

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from b import B

class A:
    def __init__(self, items):
        self.items = items

    def convert_from_b(self, b_instance: 'B') -> 'A':
        from b import B  # 局部导入用于避免循环
        return A(list(b_instance.items))

文件: b.py

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from a import A

class B:
    def __init__(self, items):
        self.items = items

    def convert_from_a(self, a_instance: 'A') -> 'B':
        from a import A  # 局部导入用于避免循环
        return B(tuple(a_instance.items))

总结

  • 将类放在同一模块:如果两个类密切相关,放在同一个模块中可以避免循环导入问题。
  • 局部导入:在需要时进行导入,可以避免在模块顶层出现循环导入。
  • 提取公共逻辑:将共享功能提取到独立模块,减少类之间的直接依赖。
  • 类型注解和延迟导入:使用类型注解和延迟导入帮助处理类型检查中的循环依赖问题。

选择最适合你项目的方案,根据代码的复杂性和结构来决定最佳的设计方式。

2024-08-26