【发布时间】:2016-12-18 20:47:05
【问题描述】:
最近我尝试过使用多类继承(在 Python 3 中),这是我以前从未做过的(从未真正使用过它)。
我很惊讶它有多“糟糕”。它的行为不像我预期的那样。我了解 MRO 和订单解决方案,但除非我遗漏了什么,否则该设计不允许在链的末端出现额外的参数。
让我举一个基本的例子:
class BaseA(object):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs.pop('is_cool', False):
self.a = True
else:
self.a = False
class FixedBaseA(object):
def __init__(self, *args, **kwargs):
if kwargs.pop('is_cool', False):
self.a = True
else:
self.a = False
# if you move super to the top (see BaseA), MyThing instances will raise an error
# Note: instead of having super down here, one could have explicitly extracted
# the keyword 'is_cool' in the definition of __init__ method
# --> __init__(self, *args, is_cool=False, **kwargs)
super().__init__(*args, **kwargs)
class BaseB(object):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs.pop('is_cool', False):
self.b = True
else:
self.b = False
class MyThing(BaseA, BaseB):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class FixedMyThing(FixedBaseA, BaseB):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class FixedMyThing2(BaseB, FixedBaseA):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class MyThing2(BaseB, BaseA):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
您可能已经猜到了,我希望的行为是:
- 无论我在继承类中为 BaseA 和 BaseB 给出什么顺序,我最终都会正确设置属性
a和b,并且没有错误 - 提供额外参数不会引发错误
当然不是这样。以下是一些测试:
def test(cls, *args, **kwargs):
try:
x = cls(*args, **kwargs)
print('{:} --> a, b = {:}, {:}'.format(cls.__name__, x.a, x.b))
except TypeError as err:
print('[ERR] {:} raised a TypeError: {:}'.format(cls.__name__, err))
test(MyThing, is_cool=True)
test(FixedMyThing, is_cool=True)
test(MyThing2, is_cool=True)
test(FixedMyThing2, is_cool=True)
test(FixedMyThing2, z=3.4)
给出:
[ERR] MyThing raised a TypeError: object.__init__() takes no parameters
FixedMyThing --> a, b = True, False
[ERR] MyThing2 raised a TypeError: object.__init__() takes no parameters
FixedMyThing2 --> a, b = True, True
[ERR] FixedMyThing2 raised a TypeError: object.__init__() takes no parameters
现在我的问题不是关于为什么会发生这种情况,有可以在网上找到的解释。虽然我无法判断这些原因,但我想知道为什么 object 类型/类在接受任何额外参数时没有表现出不同的行为。
所以真正的问题是:用下面的类替换object 是个坏主意吗?如果是,为什么?
class BaseObject(object):
def __init__(self, *args, **kwargs):
try:
# this is necessary to pass along parameters for next-in-line inherited classes
super().__init__(*args, **kwargs)
except TypeError as err:
if err.__str__().startswith('object.__init__() takes no parameters'):
pass
else:
raise
在任何地方使用此类代替object 允许交换逻辑上可以交换的类的顺序(即它们的属性/方法不重叠),并且允许使用额外的参数而不会遇到任何麻烦全部,但仍会在需要时引发错误。
MyThing --> a, b = True, True
FixedMyThing --> a, b = True, False
MyThing2 --> a, b = True, True
FixedMyThing2 --> a, b = True, True
FixedMyThing2 --> a, b = False, False
对此有任何想法都会很好。
【问题讨论】:
标签: python python-3.x class object multiple-inheritance