Class 架构:内部和外部的循环依赖 Class in Python

Class Architecture: Circular Dependency of Inner and Outer Class in Python

我的设计

口头描述

我有一个classModel,当然上面有一些方法。除此之外,我还有一个 class ModelList,其 childclass 表示 Model 的 q child class 的实例列表。除其他外,ModelList child classes 的使用是提供批量操作,这不同于仅将操作委托给 ModelList 的每个元素。因此,ModelList child classes 的目的是 "vectorize" 对应 Model class 的方法。

我也在某些地方使用了 ModelList,我希望允许 Model 的 childclass 或 ModelList 作为参数传递到一个函数。

ModelList 知道(并检查)其任何元素将被接受的类型。为了使 ModelList childclass 知道其对应的 Model child class,我将其定义为 class 变量 element_typeModelList child class.

每个ModelListchildclass都紧密耦合到一个Modelchildclass:一个ModelListclass 属于一个 Model class。这就是为什么我将 ModelList child class 作为它们各自的 Model class 的内部 classes。我的问题来了:因为 ModelList 需要知道 Model 并且 Model 需要知道 ModelList 并且在每个 class 的初始化期间,我有一个循环我的 classes 之间的依赖关系。

最小示例

我将我的代码减少到最小示例以使我的设计更容易理解:

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

不想解耦

我知道我可以通过解耦两个 classes 来摆脱循环依赖。但是我确实 想要 Model class 知道它对应的 ModelList class 我也想要 ModelList class 知道它的 Model class。每个 Model class 应该有一个并且只有一个 List 附在它上面。

猴子补丁是否合适?

我知道我可以通过 "monkeypatching" 我的 Model child class 像这样绕过依赖:

class SomeModel(Model):
    pass

class SomeModelList(ModelList):
        element_type = SomeModel

SomeModel.List = SomeModelList

我觉得这是设计缺陷的标志。我说不出为什么,但感觉 "wrong".

问题

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

如果您想让 SomeModelList 表现得像一个泛型,您应该提供元素 class 作为构造函数的参数并将其分配给那里的 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])

然后您可以通过将 class 方法添加到您的模型库 class 来概括这一点(假设您在模型之前定义了 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])