Python:用于多重继承的更好的基本"对象"



最近,我尝试使用多个类继承(在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的订单,我都将正确设置属性ab,并且没有错误
  • 给出额外的参数不会引起错误

当然不是这样。这是一些测试:

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

最新更新