避免多次打电话给Mixin班的__init__



我正在研究一个大型结构,该结构与 Mixin 一起使用,将类的函数句柄注册到为最终用户提供 API 的中心元素。我现在有 2 种不同的硬件设置,它们通常非常相似,并且具有非常大的通用功能子集,但它们都有额外的功能要注册。

因此,我将功能的公共子集放入一个抽象基类(以下示例中的"Bar")中,该基类继承自 Mixin。硬件类("Foo"和"Foo2")都继承自"Bar",但也继承自Mixin,这导致了Mixin的双重调用。init() 在 "Foo" 或 "Foo2" 的初始化阶段

from abc import ABC, abstractmethod
import inspect

class FunctionHandler:
function_register = dict()
@staticmethod
def register_function(stuff, register):
if register not in FunctionHandler.function_register.keys():
FunctionHandler.function_register[register] = list()
FunctionHandler.function_register[register].append(stuff)

class Mixin(ABC):
def __init__(self):
ABC.__init__(self)
# only call mix in the highest level of inheritance
if not inspect.stack()[2].function == "__init__":
self.mix()
@abstractmethod
def mix(self):
raise NotImplementedError
def register_stuff(self, stuff, register):
FunctionHandler.register_function(stuff, register)

class Bar(Mixin, ABC):
def __init__(self):
ABC.__init__(self)
self.name = "bar"
Mixin.__init__(self)
def mix(self):
# register a lot of stuff common to all inheriting classes
self.register_stuff("bar", self.name)
self.register_stuff("bar2", self.name)

class Foo(Bar, Mixin):
def __init__(self):
Bar.__init__(self)
self.name = "foo"
Mixin.__init__(self)
def mix(self):
Bar.mix(self)
self.register_stuff("foo", self.name)

class Foo2(Bar, Mixin):
def __init__(self):
Bar.__init__(self)
self.name = "foo2"
Mixin.__init__(self)
def mix(self):
Bar.mix(self)
self.register_stuff("foo2", self.name)
if __name__ == "__main__":
foo = Foo()
foo2 = Foo2()
print(FunctionHandler.function_register)

对于此示例,我期望的结果是:

{'foo': ['bar', 'bar2', 'foo'], 'foo2': ['bar', 'bar2', 'foo2']}

此 Mixin 的双重初始化会导致函数寄存器中出现不必要的条目,我想避免这种情况。

我当前的解决方案是,仅当调用类位于继承的最顶端(对于不是另一个子子类的基类)且具有 inspect.stack() 条件时,仅调用 Mixin 的 self.mix():

if not inspect.stack()[2].function == "__init__":
self.mix()

您可以检查,没有此条件的结果是:

{'foo2': ['bar', 'bar2', 'foo2'], 'foo': ['bar', 'bar2', 'foo'], 'bar': ['bar', 'bar2', 'foo', 'bar', 'bar2', 'foo2']}

所以总的来说,我的解决方案是有效的,但我对它并不完全满意,因为调用堆栈上的字符串比较条件对我来说似乎有点不安全。

所以对我来说有两个问题:

  1. 我的概念不是最佳的,以至于这个双混合。init() 调用首先发生?如果是,如何以更好的方式实施这种结构?

  2. 如果概念总体上没问题,有没有办法检查"继承深度",以便我可以检查它,而不是调用堆栈的函数名称?

问题不在于对Mixin.__init__()的双重调用,而是在此方法中存在mix()方法,因此它被调用了 2 次而不是 1 次(因为 Bar 调用它,然后 Foo 和 Foo2 再次调用它)。

若要删除此行为,最好在 leave 类中调用mix(),因此Foo.__init__()Foo2.__init__()方法应如下所示:

class Mixin(ABC):
def __init__(self):
ABC.__init__(self)
# end of __init__
class Foo(Bar, Mixin):
def __init__(self):
Bar.__init__(self)
self.name = "foo"
Mixin.__init__(self)
self.mix()
def mix(self):
Bar.mix(self)
self.register_stuff("foo", self.name)

结果与您想要的相同:

{'foo': ['bar', 'bar2', 'foo'], 'foo2': ['bar', 'bar2', 'foo2']}


编辑如果你想把 mix 方法留在Mixin.__init__()中,你可以将一个对象参数传递给构造函数并在其上调用 mix()。

class Mixin(ABC):
def __init__(self, obj=None):
ABC.__init__(self)
if obj and isinstance(obj, Mixin):
obj.mix()
class Foo(Bar, Mixin):
def __init__(self):
Bar.__init__(self)
self.name = "foo"
Mixin.__init__(self, self)

输出它仍然是一样的

最新更新