具有不同初始化参数的多重继承的真正解决方案
The real solution for multiple inheritance with different init parameters
这看起来是个简单的任务,但我现在在这上面花了太多时间,却没有解决方案。这是设置:
class A(object):
def __init__(self, x=0):
print("A.__init__(x=%d)" % x)
class B(object):
def __init__(self, y=1):
print("B.__init__(y=%d)" % y)
class C(A, B):
def __init__(self, x=2, y=3, z=4):
super().__init__(x=x, y=y)
print("C.__init__(z=%d)" % z)
这就是想法,但当然这会导致
TypeError: __init__() got an unexpected keyword argument 'y'
所有其他尝试都以类似方式失败,我可以在 Internet 上找到 no 资源和正确的解决方案。唯一的解决方案包括用 *args, **kwargs
替换所有初始化参数。这不太符合我的需求。
根据要求,一个真实世界的例子:
(这使用了不同的方法,它具有有效的语法,但会产生不需要的结果。)
from PyQt5.QtCore import QObject
class Settings(object):
def __init__(self, file):
self.file = file
class SettingsObject(object):
def __init__(self, settings=None):
print("Super Init", settings is None)
self.settings = settings
class MyObject(QObject, SettingsObject):
def __init__(self, param):
print("Entering Init")
QObject.__init__(self)
SettingsObject.__init__(self, settings=Settings(__file__))
self.param = param
print("Leaving Init")
结果:
Entering Init
Super Init True
Super Init False
Leaving Init
我希望 Super Init True
行消失。
您似乎对一些事情感到困惑,所以:
为什么会得到TypeError: __init__() got an unexpected keyword argument 'y'
?
因为方法解析顺序(MRO)中__init__
的下一个实现是A.__init__
,它只接受x
。 MRO 是 C
-> A
-> B
,因此你必须让 A.__init__
接受 y
(具体或使用 **kwargs
)并且将其传递(再次使用 super
)给 B.__init__
。
super
不会调用 every 实现,当然也不能确保他们都只得到他们期望的参数,它只是调用 next 实现所有给定的参数。
为什么 SettingsObject.__init__
被调用两次,一次有 settings
,一次没有 settings
?
QObject.__init__
似乎包含对 super().__init__
的调用,它会调用 SettingsObject.__init__
,因为它是 MyObject
的 MRO 中的下一个。但是它不传递任何 settings
参数,因此第一次调用它时您会看到 settings is None
。第二次直接调用它,并显式传递 settings
,所以你会看到 settings is not None
.
你怎么写mix-in classes?
我认为这是你真正应该问的问题。 SettingsObject
应该是 mix-in class,因此设计正确,可以与它所混入的层次结构中的其他 classes 协作。在这种情况下:
class SettingsObject: # (object) isn't needed in 3.x
def __init__(self, *args, settings=None, **kwargs):
super().__init__(*args, **kwargs)
self.settings = settings
从你的例子看来 QObject.__init__
没有任何必需的参数,但你仍然应该编写混入以在可选参数或在其他地方重用的情况下发挥良好的作用。然后 MyObject
实现看起来像:
class MyObject(SettingsObject, QObject):
def __init__(self, param):
super().__init__(settings=Settings(file))
self.param = param
注意:
- mix-in classes在subclassing时最先出现,所以MRO是
MyObject
-> SettingsObject
-> QObject
;和
- 您调用
super().__init__
一次,而不是单独调用每个超级class 实现。
作为替代方案,您是否考虑过组合?也许 MyObject
应该 采用 设置:
class MyObject(QObject):
def __init__(self, param, settings=None):
super().__init__()
self.param = param
self.settings = settings
obj = MyObject('something', Settings(__file__))
现在您调用 self.settings.method(...)
而不是 self.method(...)
,但您没有多重继承引入的复杂性。
这看起来是个简单的任务,但我现在在这上面花了太多时间,却没有解决方案。这是设置:
class A(object):
def __init__(self, x=0):
print("A.__init__(x=%d)" % x)
class B(object):
def __init__(self, y=1):
print("B.__init__(y=%d)" % y)
class C(A, B):
def __init__(self, x=2, y=3, z=4):
super().__init__(x=x, y=y)
print("C.__init__(z=%d)" % z)
这就是想法,但当然这会导致
TypeError: __init__() got an unexpected keyword argument 'y'
所有其他尝试都以类似方式失败,我可以在 Internet 上找到 no 资源和正确的解决方案。唯一的解决方案包括用 *args, **kwargs
替换所有初始化参数。这不太符合我的需求。
根据要求,一个真实世界的例子:
(这使用了不同的方法,它具有有效的语法,但会产生不需要的结果。)
from PyQt5.QtCore import QObject
class Settings(object):
def __init__(self, file):
self.file = file
class SettingsObject(object):
def __init__(self, settings=None):
print("Super Init", settings is None)
self.settings = settings
class MyObject(QObject, SettingsObject):
def __init__(self, param):
print("Entering Init")
QObject.__init__(self)
SettingsObject.__init__(self, settings=Settings(__file__))
self.param = param
print("Leaving Init")
结果:
Entering Init
Super Init True
Super Init False
Leaving Init
我希望 Super Init True
行消失。
您似乎对一些事情感到困惑,所以:
为什么会得到
TypeError: __init__() got an unexpected keyword argument 'y'
?因为方法解析顺序(MRO)中
__init__
的下一个实现是A.__init__
,它只接受x
。 MRO 是C
->A
->B
,因此你必须让A.__init__
接受y
(具体或使用**kwargs
)并且将其传递(再次使用super
)给B.__init__
。super
不会调用 every 实现,当然也不能确保他们都只得到他们期望的参数,它只是调用 next 实现所有给定的参数。为什么
SettingsObject.__init__
被调用两次,一次有settings
,一次没有settings
?QObject.__init__
似乎包含对super().__init__
的调用,它会调用SettingsObject.__init__
,因为它是MyObject
的 MRO 中的下一个。但是它不传递任何settings
参数,因此第一次调用它时您会看到settings is None
。第二次直接调用它,并显式传递settings
,所以你会看到settings is not None
.你怎么写mix-in classes?
我认为这是你真正应该问的问题。
SettingsObject
应该是 mix-in class,因此设计正确,可以与它所混入的层次结构中的其他 classes 协作。在这种情况下:class SettingsObject: # (object) isn't needed in 3.x def __init__(self, *args, settings=None, **kwargs): super().__init__(*args, **kwargs) self.settings = settings
从你的例子看来
QObject.__init__
没有任何必需的参数,但你仍然应该编写混入以在可选参数或在其他地方重用的情况下发挥良好的作用。然后MyObject
实现看起来像:class MyObject(SettingsObject, QObject): def __init__(self, param): super().__init__(settings=Settings(file)) self.param = param
注意:
- mix-in classes在subclassing时最先出现,所以MRO是
MyObject
->SettingsObject
->QObject
;和 - 您调用
super().__init__
一次,而不是单独调用每个超级class 实现。
- mix-in classes在subclassing时最先出现,所以MRO是
作为替代方案,您是否考虑过组合?也许 MyObject
应该 采用 设置:
class MyObject(QObject):
def __init__(self, param, settings=None):
super().__init__()
self.param = param
self.settings = settings
obj = MyObject('something', Settings(__file__))
现在您调用 self.settings.method(...)
而不是 self.method(...)
,但您没有多重继承引入的复杂性。