最近我尝试使用多类继承(在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)
正如您可能猜到的那样,我希望的行为是:
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
对此的任何想法都会很好。
答案 0 :(得分:0)
首先,在Python 3中,您不需要继承object
。那仅适用于Python 2;现在已经隐瞒了。
我想知道为什么对象类型/类通过接受任何额外参数而表现不同。
我不确定你的意思。
object
不接受任何参数,那是因为它对它们毫无用处。提出错误总是比默默地忽略意外情况更好。 (“错误不应该默默地传递。除非明确地沉默。” - Python的禅宗)*args, **kwargs
明确地允许他们。所以真正的问题是:用下面的类替换对象是个坏主意吗?如果是,为什么?
是的,因为你默默地忽略了错误。
通常,建议不要使用*args, **kwargs
,除非您使用了大量参数,或者您正在编写中间件库(即使用您的参数将库中的参数传递给您使用的库)。 / p>
相反,你应该像这样重写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