python:用于多重继承的更好的基础'对象'

时间:2016-12-18 20:47:05

标签: python python-3.x class object multiple-inheritance

最近我尝试使用多类继承(在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提供什么顺序,我最终都会正确设置属性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

对此的任何想法都会很好。

1 个答案:

答案 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