【问题标题】:How to import modules containing classes with circular dependency?如何导入包含具有循环依赖的类的模块?
【发布时间】:2020-02-11 13:52:28
【问题描述】:

假设我有以下基础和孩子

class Base:

    def __new__(cls, *args):
        if cls is Base:
            if len(args) < 2:
                return Child1.__new__(Child1, *args)

            return Child2.__new__(Child2, *args)

        return super().__new__(cls)

    def __init__(self, arg):
        self.common_arg = arg


class Child1(Base):
    def __init__(self, arg0=None):
        super().__init__(arg0)



class Child2(Base):
    def __init__(self, arg0, arg1, *args):
        super().__init__(arg0 + arg1)

        self.args = list(args).copy()

类之间显然存在循环依赖关系,但是,只要所有类都定义在同一个模块中,这不会导致任何问题。

现在,我应该如何将它们分成三个模块(在同一个包中)?

我做了三个文件的拆分:

package/
    __init__.py
    base.py
    ch1.py
    ch2.py

内容如下:

# base.py ############################################################

from . import ch1, ch2

class Base:

    def __new__(cls, *args):
        if cls is Base:
            if len(args) < 2:
                return ch1.Child1.__new__(ch1.Child1, *args)

            return ch2.Child2.__new__(ch2.Child2, *args)

        return super().__new__(cls)

    def __init__(self, arg):
        self.common_arg = arg


# ch1.py ############################################################
from . import base

class Child1(base.Base):
    def __init__(self, arg0=None):
        super().__init__(arg0)

# ch2.py ############################################################
from . import base


class Child2(base.Base):
    def __init__(self, arg0, arg1, *args):
        super().__init__(arg0 + arg1)
        self.args = list(args).copy()   

按照here 的建议,但它不起作用。

import package.ch1

加注

AttributeError: module 'package.base' has no attribute 'Base'

【问题讨论】:

  • 不确定这是否是完全相同的问题,但我曾经遇到过依赖问题,我发现经典的 hack 将导入放在一个不带参数的函数中,只包含导入语句。
  • 您不能导入使用需要其他尚未定义的类的类的模块。几乎可以肯定,您可以更有效地设计它。利用基类中的方法默认被继承类覆盖的事实,您不需要这种令人困惑的设计。
  • 通常情况下,使用父类必须了解其子类及其实现的架构是一种反模式。你想用这个技巧解决什么具体任务?
  • 无论你可以使用什么 hack,它都不能解决根本问题,即你的设计是错误的(wrt:基本的 OO 设计 - 基类不应该知道它们的子类 - 和依赖项处理 - 即使语言支持它,你仍然不应该有任何循环依赖。明显的 XY 问题(你问的是如何使错误的解决方案工作而不是解释问题背后 这个解决方案以及如何解决它),所以请编辑您的问题并解释实际用例,以便有人可能会提出更好的设计建议。
  • 我同意你们所有人的观点,这种设计模式很复杂,尽管这是已经使用过的东西,例如在scipy.signal.lti (docs.scipy.org/doc/scipy/reference/generated/…) 中。我使用这种模式的原因如下:Child1Child2Base 的不同表示。它们会有很多通用的方法,但是方法的实现是不同的。客户端只需实例化Base 并让代码找出哪个表示更适合Child1Child2 来解决该问题。

标签: python python-3.x python-module


【解决方案1】:

让您的用户调用工厂函数:

def make_base(*args):
    if len(args) < 2:
        return Child1(*args)

    return Child2(*args)


class Base:
    def __init__(self, arg):
        self.common_arg = arg


class Child1(Base):
    pass        # Child1 automatically inherits Base.__init__()


class Child2(Base):
    def __init__(self, arg0, arg1, *args):
        super().__init__(arg0 + arg1)

        self.args = list(args).copy()

现在上面代码的每一部分都可以拆分成自己的文件了。

【讨论】:

  • 谢谢。为了澄清,我必须补充一点,如果我们要将代码拆分为单独的文件,工厂函数make_base 和基类Base 的定义不能在同一个文件中。 Base 和工厂函数需要两个单独的文件。
  • 是的,我确实是这个意思。有一个函数和三个类。它们可以分成四个文件。
猜你喜欢
  • 2019-02-02
  • 1970-01-01
  • 2011-09-15
  • 2016-07-21
  • 2018-04-01
  • 2022-01-21
  • 1970-01-01
  • 2014-04-28
相关资源
最近更新 更多