最近,我尝试使用多个类继承(在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和BASBB的订单,我都将正确设置属性
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 3中,您无需从object
继承。那只是Python 2;它现在隐含地完成了。
我想知道为什么对象类型/类接受任何附加参数的行为不同。
我不确定你在这里是什么意思。
- 如果您在问为什么
object
不采用任何参数,那是因为它们对它们没有用。而且提出错误总是比默默地忽略意外情况更好。(" 错误绝不应该默默通过。除非明确沉默。" - python的禅宗) - 如果您在问为什么您的课程接受额外的论点:这是因为您明确允许他们使用
*args, **kwargs
。
所以真正的问题是:通过以下类替换对象是一个坏主意吗?如果是,为什么?
是的,因为您默默地忽略了错误。
通常,不建议使用 *args, **kwargs
,除非您使用很多参数,否
相反,您应该像这样重写BaseA
:
class BaseA(object):
def __init__(self, is_cool=False):
super().__init__()
if is_cool:
self.a = True
else:
self.a = False
您的其他类也是如此。
现在,更多的主题备注:
您可以进一步清理BaseA
,因为您正在使用Python 3:
class BaseA(object):
def __init__(self, is_cool=False):
if is_cool:
self.a = True
else:
self.a = False
,您甚至可以删除不必要的分解:
class BaseA(object):
def __init__(self, is_cool=False):
self.a = is_cool