Multiple inheritance - How to call method_x in InheritedBaseB from method_x in InheritedBaseA?

The Music Guy musicguy at alphaios.net
Sat Sep 5 09:46:17 CEST 2009


On Fri, Sep 4, 2009 at 11:23 AM, Scott David
Daniels<Scott.Daniels at acm.org> wrote:
> The Music Guy wrote:
>>
>> I have a peculiar problem that involves multiple inheritance and method
>> calling.
>>
>> I have a bunch of classes, one of which is called MyMixin and doesn't
>> inherit from anything. MyMixin expects that it will be inherited along
>> with one of several other classes that each define certain
>> functionality.
>
> ... <code semi-example> ...
>>
>> This all appears fine at first, but ...
>> One might be tempted to amend MyMixin's method_x so that it calls the
>> parent's method_x before doing anything else:
>>
>> class MyMixin(object):
>>    def method_x(self, a, b, c):
>>        super(MyMixin, self).method_x(a, b, c)
>>        ...
>>
>> ...but of course, that will fail with an AttributeError because
>> MyMixin's only superclass is object, which does not have a method_x.
>
> Here the fix below works.
>
>> The only way I can think to solve the problem would be to implement a
>> method_x for each Foo that calls the method_x for each of the bases:
>
> ...
>>
>> So, does anyone have an idea about how to remedy this, or at least
>> work around it?
>
> The diamond inheritance stuff is meant to allow you to deal with
> exactly this issue.  If you define a class, MixinBase, with do-
> nothing entries for all the methods you are inventing, and you
> make all of your Mixin classes (and your main class) inherit
> from MixinBase, you are guaranteed that all of the Mixins you
> use will be earlier on the method resolution order (mro in the
> docs) than MixinBase.  If the set of actual methods is small
> and pervasive, I might even be tempted rename MixinBase to
> "Object":
>
>>>> if 1:
>    class MixinBase(object):
>        '''Base for solving mixin strategy.
>
>        Also a nice common place to describe the args and meaning.
>        '''
>        def method_x(self, a, b, c):
>            '''Suitable docstring'''
>            print 'MixinBase'
>
>    class MyMixin(MixinBase):
>        def method_x(self, a, b, c):
>            super(MyMixin, self).method_x(a, b, c)
>            print 'Mixin'
>    class BaseA(MixinBase):
>        def method_x(self, a, b, c):
>            super(BaseA, self).method_x(a, b, c)
>            print 'BaseA'
>    class BaseB(MixinBase):
>        pass
>    class BaseC(MixinBase):
>        def method_x(self, a, b, c):
>            super(BaseC, self).method_x(a, b, c)
>            print 'BaseC'
>    class FooX(MyMixin, BaseA):
>        def method_x(self, a, b, c):
>            super(FooX, self).method_x(a, b, c)
>            print 'FooX'
>    class FooY(MyMixin, BaseB):
>        pass
>    class FooZ(MyMixin, BaseC):
>        def method_x(self, a, b, c):
>            super(FooZ, self).method_x(a, b, c)
>            print 'FooZ'
>
>
>>>> FooZ().method_x(1,2,3)
> MixinBase
> BaseC
> Mixin
> FooZ
>>>> FooY().method_x(1,2,3)
> MixinBase
> Mixin
>>>> FooX().method_x(1,2,3)
> MixinBase
> BaseA
> Mixin
> FooX
>>>> BaseA().method_x(1,2,3)
> MixinBase
> BaseA
>>>>
> --Scott David Daniels
> Scott.Daniels at Acm.Org
> --
> http://mail.python.org/mailman/listinfo/python-list
>

Thanks for your reply, Scott. I'm glad there's somebody here who is
actually willing to help me with this; it seems like I usually don't
get any response from posting to most mailing lists. However, I'm not
sure I completely understand your solution.

Two of my requirements are that 1.) no Foo class ever need implement a
method_x because each FooN is merely a version of a BaseN with MyMixin
features added in, and 2.) Each BaseN is a fully-usable class that
does not have MyMixin features and does not need to be subclassed in
order to be used (ie. even though it is usable as a base, it is also
usable as an ordinary class).

Here's some psuedocode that may make my intent more clear:

class Widget(SomeBase, SomeOtherBase, ...):
    """ A GUI class that occupies a region of a screen. Can be
contained by a Container class. """

    def __init__(self, region):

        # Rect-ify the given region (no pun intended)
        self.region = Rect(region)

class Container(Widget):
   """ A widget that can contain other widgets and show their regions
in relation to the container's region. """

    def __init__(self, region, children=()):
        Widget.__init__(self, region)

        self.children = []

        for child in children:
            self.append(child)

    def __getitem__(self, index):
        return self.children[index]

    def __setitem__(self, index, new):
        self.children[index] = new

    def append(self, new):
        self.children.append(new)

class MovableContainer(Container):
    """ Enhanced container that can be moved in response to the user
clicking and dragging on an empty space in the container. """

    ...code for handling mouse input...

    def append(self, child):
        """ Provides enhanced functionality for Container.append. """
        super(MovableContainer, self).append(child)
        ... do some other stuff ...

class LayoutControlMixin(object):
    """ A mixin that can be used to add automatic child widget layout
control to existing container classes. """

    def append(self, child, **child_layout_config):
       """ Same as Container.append or MovableContainer.append, but
allows for optional layout settings specific to each child widget. """

        ### This is the key line; it should call the `append` method
of the "other" superclass (Container, MovableContainer, etc.) ###
        get_other_superclass(self).append(self, child)

        ...process child_layout_config data, perhaps modify regions of
children...

class LayoutContainer(LayoutControlMixin, Container):
    """ A version of Container with layout control. """
    pass

class MovableLayoutContainer(LayoutControlMixin, MovableContainer):
    """ A version of MovableContainer with layout control. """
    pass



More information about the Python-list mailing list