我正在处理一个大型结构,该结构正在与Mixin一起将类的函数句柄注册到向最终用户提供API的中央元素上。我现在有2种不同的硬件设置,它们总体上非常相似,并且具有非常大的通用功能子集,但是它们两者都有要注册的附加功能。
因此,我将功能的公共子集放入了继承自Mixin的抽象基类(在下面的示例中为“ Bar”)。硬件类(“ 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']}
因此,一般而言,我的解决方案有效,但是我并不完全满意,因为调用堆栈上的字符串比较条件对我来说似乎有点不安全。
所以对我来说有两个问题:
我的概念是否不是最优的,以至于这种双重Mixin。 init ()调用首先发生?如果可以,如何更好地实现这种结构?
如果总体上这个概念还可以,有没有办法检查“继承深度”,以便我可以检查它,而不是调用堆栈的函数名?
答案 0 :(得分:0)
问题不在于对Mixin.__init__()
的双重调用,而是在此方法中有mix()
方法,因此它被调用了2次而不是1次(因为Bar调用了,并且然后Foo和Foo2再次调用它)。
要删除此行为,最好在叶子类中调用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)
输出仍然相同