【问题标题】:Class Architecture: Circular Dependency of Inner and Outer Class in Python类架构:Python中内部类和外部类的循环依赖
【发布时间】:2020-01-03 16:56:59
【问题描述】:

我的设计

文字描述

我有一个班级Model,当然上面有一些方法。除此之外,我还有一个类ModelList,其子类表示Model 的q 个子类的实例列表。除此之外,ModelList 子类的使用是为了提供批量操作,这与仅将操作委托给ModelList 的每个元素不同。因此,ModelList 子类的目的是“矢量化”相应Model 类的方法。

我还在某些地方使用ModelList,我希望允许ModelModelList 的子类作为参数传递给函数。

ModelList 知道(并检查)其任何元素将接受的类型。为了让ModelList 子类知道其对应的Model 子类,我将其定义为ModelList 子类上的类变量element_type

每个ModelList 子类都与Model 子类紧密耦合:一个ModelList 类属于一个Model 类。这就是为什么我将ModelList 子类作为内部类放到它们各自的Model 类中。我的问题来了:因为ModelList 需要知道ModelModel 需要知道ModelList 并且在每个类的初始化过程中,我的类之间都有循环依赖关系。

最小示例

为了让我的设计更易于理解,我将代码缩减为最小示例:

class Model(ABC):
    pass
class ModelList(list):
    @classmethod
    def __init__(self, elements=None):
        elements = list() if not elements else elements

        for value in elements:
            self._check_type(value)

        list.__init__(self, elements)

    def _check_type(self, val):
        if not isinstance(val, self.__class__.element_type):
            raise TypeError(
            f"{self.__class__} accepts only instances of {self.__class__.element_type} as elements. `{val}` is not!") 

以下导致错误free variable 'SomeModel' referenced before assignment in enclosing scope

class SomeModel(Model):
    class List(ModelList):
        element_type = SomeModel  # this causes the Error

我不想解耦

我知道我可以通过解耦这两个类来摆脱循环依赖。但我确实想要Model 类知道其对应的ModelList 类,并且我希望ModelList 类知道它的Model 类。每个Model 类都应该有一个且只有一个List 附加到它上面。

猴子补丁合适吗?

我知道我可以像这样通过“monkeypatching”我的Model 子类来规避依赖:

class SomeModel(Model):
    pass

class SomeModelList(ModelList):
        element_type = SomeModel

SomeModel.List = SomeModelList

对我来说,这感觉像是设计缺陷的标志。我不能说为什么,但感觉“不对”。

问题

  1. 猴子补丁在这里合适吗?或者它是否表明我的设计存在更深层次的概念问题?
  2. 还有哪些其他解决方案?
  3. 如何重新设计以摆脱这种循环依赖(但仍保持类的耦合)?
  4. 是否可以在稍后定义相应的Model 子类时评估element_type

【问题讨论】:

  • 您似乎想要创建一个类型安全的列表,但请注意,您仍然可以通过使用 append、insert、+ 或 += 将另一种类型的对象添加到列表类中。
  • @sloth 谢谢,我知道这一点,我已经添加了所有神奇的方法来确保这些列表操作将生成ModelList 子类的实例。为了创建一个最小的示例,我只是在这里没有提及它们。我想,我也应该放弃list的继承来创造一个真正的ME。
  • 是的,所有这些问题都在告诉您设计存在缺陷,但您事先已经知道,并且您说您希望它有缺陷。如果这种设计被认为有缺陷的原因对你来说并不重要,或者它给你带来了比潜在问题更有价值的东西,那就继续吧。

标签: python oop design-patterns monkeypatching decoupling


【解决方案1】:

如果你想让 SomeModelList 表现得像一个泛型,你应该提供元素类作为构造函数的参数并将它分配给 self.element_type 那里。

class ModelList(list):

    def __init__(self, model, elements=None):
        self.element_Type = model 
        elements = list() if not elements else elements

        for value in elements:
            self._check_type(value)

        list.__init__(self, elements)

    def _check_type(self, val):
        if not isinstance(val, self.element_type):
            raise TypeError(
            f"{self.__class__} accepts only instances of {self.element_type} as elements. `{val}` is not!") 


# usage

modelList = ModelList(SomeModel,[instance1,instance2,instance3])

然后您可以通过向您的 Model 基类添加一个类方法来概括这一点(假设您在 Model 之前定义 ModelList):

class Model:

    ... your other methods ...

    @classmethod
    def List(self,elements=None):
        return ModelList(self.__class__,elements)

# usage

class SomeModel(Model): pass

modelList = SomeModel.List([instance1,instance2,instance3]) 

【讨论】:

  • 添加了如何在 Model 类中泛化使用 ModelList 的示例。
  • 我怎么能自己找到解决方案?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-03
  • 1970-01-01
  • 2011-09-18
  • 2013-04-26
  • 1970-01-01
  • 2013-02-24
  • 2011-03-10
相关资源
最近更新 更多