On Fri, Sep 4, 2009 at 11:23 AM, Scott David Daniels<scott.dani...@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.dani...@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 -- http://mail.python.org/mailman/listinfo/python-list