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

T

The Music Guy

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. It defines method_x, which it assumes will also be
defined in the other class that MyMixin ends up getting inherited
with. For example,

class MyMixin(object):
def method_x(self, a, b, c):
...

class BaseA(object):
def method_x(self, a, b, c):
...

class BaseB(object):
def method_x(self, a, b, c):
...

class BaseC(object):
def method_x(self, a, b, c):
...

class FooX(MyMixin, BaseA):
...

class FooY(MyMxin, BaseB):
...

class FooZ(MyMixin, BaseC):
...


This all appears fine at first, but there is a problem: Each Foo's
method_x must call the method_x of MyMixin as well as the method_x of
each respective Foo's second base class. One cannot simply call
FooN.method_x, because that will only call MyMixin.method_x and not
that of the other base.

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.

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:

class FooX(MyMixin, BaseA):
def method_x(self, a, b, c):
MyMixin.method_x(self, a, b, c)
BaseA.method_x(self, a, b, c)

class FooY(MyMxin, BaseB):
def method_x(self, a, b, c):
MyMixin.method_x(self, a, b, c)
BaseB.method_x(self, a, b, c)

class FooZ(MyMixin, BaseC):
def method_x(self, a, b, c):
MyMixin.method_x(self, a, b, c)
BaseC.method_x(self, a, b, c)


The problem with this solution is that method_x has to be explicitly
created for each Foo, even though they all do just about the same
thing, which kind of defeats the purpose of using multiple inheritance
in this situation. Besides that, I just don't like it!

So, does anyone have an idea about how to remedy this, or at least
work around it?
 
T

The Music Guy

Here the fix below works.


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":

   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'


--Scott David Daniels
(e-mail address removed)

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
 
R

ryles

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. It defines method_x, which it assumes will also be
defined in the other class that MyMixin ends up getting inherited
with. For example,

class MyMixin(object):
    def method_x(self, a, b, c):
        ...

class BaseA(object):
    def method_x(self, a, b, c):
        ...

class BaseB(object):
    def method_x(self, a, b, c):
        ...

class BaseC(object):
    def method_x(self, a, b, c):
        ...

class FooX(MyMixin, BaseA):
    ...

class FooY(MyMxin, BaseB):
    ...

class FooZ(MyMixin, BaseC):
    ...

This all appears fine at first, but there is a problem: Each Foo's
method_x must call the method_x of MyMixin as well as the method_x of
each respective Foo's second base class. One cannot simply call
FooN.method_x, because that will only call MyMixin.method_x and not
that of the other base.

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.

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:

class FooX(MyMixin, BaseA):
    def method_x(self, a, b, c):
        MyMixin.method_x(self, a, b, c)
        BaseA.method_x(self, a, b, c)

class FooY(MyMxin, BaseB):
    def method_x(self, a, b, c):
        MyMixin.method_x(self, a, b, c)
        BaseB.method_x(self, a, b, c)

class FooZ(MyMixin, BaseC):
    def method_x(self, a, b, c):
        MyMixin.method_x(self, a, b, c)
        BaseC.method_x(self, a, b, c)

The problem with this solution is that method_x has to be explicitly
created for each Foo, even though they all do just about the same
thing, which kind of defeats the purpose of using multiple inheritance
in this situation. Besides that, I just don't like it!

So, does anyone have an idea about how to remedy this, or at least
work around it?

Hopefully I've interpreted your requirement correctly.

In this particular case a workaround is to use super() in your base
classes and then have your concrete classes inherit from the base
*before* the mixin:

class MyMixin(object):
def method_x(self, a, b, c):
print "MyMixin"

class BaseA(object):
def method_x(self, a, b, c):
print "BaseA"
super(BaseA, self).method_x(a, b, c)

class FooX(BaseA, MyMixin):
pass

FooX().method_x(1, 2, 3)

BaseA
MyMixin


And if you want to define method_x() in FooX, then simply have that
use super() as well.
 
C

Carl Banks

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. It defines method_x, which it assumes will also be
defined in the other class that MyMixin ends up getting inherited
with. For example,

class MyMixin(object):
    def method_x(self, a, b, c):
        ...

class BaseA(object):
    def method_x(self, a, b, c):
        ...

class BaseB(object):
    def method_x(self, a, b, c):
        ...

class BaseC(object):
    def method_x(self, a, b, c):
        ...

class FooX(MyMixin, BaseA):
    ...

class FooY(MyMxin, BaseB):
    ...

class FooZ(MyMixin, BaseC):
    ...

This all appears fine at first, but there is a problem: Each Foo's
method_x must call the method_x of MyMixin as well as the method_x of
each respective Foo's second base class. One cannot simply call
FooN.method_x, because that will only call MyMixin.method_x and not
that of the other base.

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.

Out of curiosity, did you try this and are reporting that it resulted
in an AttributeError, or did you merely deduce that it would raise
AttributeError based on your knowledge of Python's inheritance?

I ask this rhetorically. I know that you didn't try it (or that you
tried it and made a mistake) because if you had tried it (and not made
a mistake) you would have seen that it works exactly as you want it
to.


Carl Banks
 
T

The Music Guy

Out of curiosity, did you try this and are reporting that it resulted
in an AttributeError, or did you merely deduce that it would raise
AttributeError based on your knowledge of Python's inheritance?

I ask this rhetorically.  I know that you didn't try it (or that you
tried it and made a mistake) because if you had tried it (and not made
a mistake) you would have seen that it works exactly as you want it
to.


Carl Banks

This code causes an error:

#!/usr/bin/python

def main():
foox = FooX()
fooy = FooY()
fooz = FooZ()

#foox.method_x("I", "AM", "X")
print
fooy.method_x("ESTOY", "Y", "!")
print
fooz.method_x(100, 200, 300)


class MyMixin(object):

def method_x(self, a, b, c):
super(MyMixin, self).method_x(a, b, c)
print "MyMixin.method_x(%s, %s, %s, %s)" % (repr(self),
repr(a), repr(b), repr(c))

class BaseA(object):

def method_x(self, a, b, c):
super(BaseA, self).method_x(a, b, c)
print "BaseA.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class BaseB(object):

pass

class BaseC(object):

def method_x(self, a, b, c):
super(BaseC, self).method_x(a, b, c)
print "BaseC.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class FooX(BaseA, MyMixin):

def method_x(self, a, b, c):
super(FooX, self).method_x(a, b, c)
print "FooX.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class FooY(BaseB, MyMixin):

pass

class FooZ(BaseC, MyMixin):

def method_x(self, a, b, c):
super(FooZ, self).method_x(a, b, c)
print "FooZ.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

if __name__ == '__main__':
main()


Traceback (most recent call last):
File "foo.py", line 54, in <module>
main()
File "foo.py", line 10, in main
fooy.method_x("ESTOY", "Y", "!")
File "foo.py", line 18, in method_x
super(MyMixin, self).method_x(a, b, c)
AttributeError: 'super' object has no attribute 'method_x'
 
T

The Music Guy

Sorry, that last code had a typo in it:

#!/usr/bin/python

def main():
foox = FooX()
fooy = FooY()
fooz = FooZ()

foox.method_x("I", "AM", "X")
print
fooy.method_x("ESTOY", "Y", "!")
print
fooz.method_x(100, 200, 300)


class MyMixin(object):

def method_x(self, a, b, c):
super(MyMixin, self).method_x(a, b, c)
print "MyMixin.method_x(%s, %s, %s, %s)" % (repr(self),
repr(a), repr(b), repr(c))

class BaseA(object):

def method_x(self, a, b, c):
super(BaseA, self).method_x(a, b, c)
print "BaseA.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class BaseB(object):

pass

class BaseC(object):

def method_x(self, a, b, c):
super(BaseC, self).method_x(a, b, c)
print "BaseC.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class FooX(BaseA, MyMixin):

def method_x(self, a, b, c):
super(FooX, self).method_x(a, b, c)
print "FooX.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class FooY(BaseB, MyMixin):

pass

class FooZ(BaseC, MyMixin):

def method_x(self, a, b, c):
super(FooZ, self).method_x(a, b, c)
print "FooZ.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

if __name__ == '__main__':
main()


Traceback (most recent call last):
File "foo.py", line 54, in <module>
main()
File "foo.py", line 8, in main
foox.method_x("I", "AM", "X")
File "foo.py", line 40, in method_x
super(FooX, self).method_x(a, b, c)
File "foo.py", line 24, in method_x
super(BaseA, self).method_x(a, b, c)
File "foo.py", line 18, in method_x
super(MyMixin, self).method_x(a, b, c)
AttributeError: 'super' object has no attribute 'method_x'
 
C

Carl Banks

Sorry, that last code had a typo in it:

#!/usr/bin/python

def main():
    foox = FooX()
    fooy = FooY()
    fooz = FooZ()

    foox.method_x("I", "AM", "X")
    print
    fooy.method_x("ESTOY", "Y", "!")
    print
    fooz.method_x(100, 200, 300)

class MyMixin(object):

    def method_x(self, a, b, c):
        super(MyMixin, self).method_x(a, b, c)
        print "MyMixin.method_x(%s, %s, %s, %s)" % (repr(self),
repr(a), repr(b), repr(c))

class BaseA(object):

    def method_x(self, a, b, c):
        super(BaseA, self).method_x(a, b, c)
        print "BaseA.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class BaseB(object):

    pass

class BaseC(object):

    def method_x(self, a, b, c):
        super(BaseC, self).method_x(a, b, c)
        print "BaseC.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class FooX(BaseA, MyMixin):

    def method_x(self, a, b, c):
        super(FooX, self).method_x(a, b, c)
        print "FooX.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class FooY(BaseB, MyMixin):

    pass

class FooZ(BaseC, MyMixin):

    def method_x(self, a, b, c):
        super(FooZ, self).method_x(a, b, c)
        print "FooZ.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

if __name__ == '__main__':
    main()

Traceback (most recent call last):
  File "foo.py", line 54, in <module>
    main()
  File "foo.py", line 8, in main
    foox.method_x("I", "AM", "X")
  File "foo.py", line 40, in method_x
    super(FooX, self).method_x(a, b, c)
  File "foo.py", line 24, in method_x
    super(BaseA, self).method_x(a, b, c)
  File "foo.py", line 18, in method_x
    super(MyMixin, self).method_x(a, b, c)
AttributeError: 'super' object has no attribute 'method_x'

That's not what you did in your original post, though.

Mixins should be listed first among bases, which is how you did it in
your original post, and how it had to be in order for it to "just
work" as I claimed.

class FooX(MyMixin, BaseA)
class FooY(MyMixin, BaseB)
class FooZ(MyMixin, BaseC)



If you are concerned that a base class might not define method_x (and
therefore that the super call from MyMixin would fail), then there are
a couple things you can do. First thing is to consider whether you
ought to be using two method instead of one.

Otherwise the easiest thing is to catch and ignore AttributeError from
MyMixin's method_x:

class MyMixin(object):
def method_x(self, a, b, c):
try:
f = super(MyMixin, self).method_x
except AttributeError:
pass
else:
f(a,b,c)

Note that you don't want to call the super inside the try block
otherwise you risk catching spurrious AttributeErrors.

This strategy might hide some typo-style errors with the base class,
so if you're worried about that, another strategy would be to define
another mixin which provides an empty method_x:

class ProvideMethodX(self):
def method_x(self, a, b, c):
pass

Then if BaseB does not define method_x, derive FooY from it like this:

class FooY(MyMixin,ProvideMethodX,BaseB)

Finally (and probably least confusingly) you could use separate mixins
for bases that define method_x and those that don't.


Carl Banks
 
T

The Music Guy

That's not what you did in your original post, though.

Mixins should be listed first among bases, which is how you did it in
your original post, and how it had to be in order for it to "just
work" as I claimed.

class FooX(MyMixin, BaseA)
class FooY(MyMixin, BaseB)
class FooZ(MyMixin, BaseC)



If you are concerned that a base class might not define method_x (and
therefore that the super call from MyMixin would fail), then there are
a couple things you can do.  First thing is to consider whether you
ought to be using two method instead of one.

Otherwise the easiest thing is to catch and ignore AttributeError from
MyMixin's method_x:

class MyMixin(object):
   def method_x(self, a, b, c):
       try:
           f = super(MyMixin, self).method_x
       except AttributeError:
           pass
       else:
           f(a,b,c)

Note that you don't want to call the super inside the try block
otherwise you risk catching spurrious AttributeErrors.

This strategy might hide some typo-style errors with the base class,
so if you're worried about that, another strategy would be to define
another mixin which provides an empty method_x:

class ProvideMethodX(self):
   def method_x(self, a, b, c):
       pass

Then if BaseB does not define method_x, derive FooY from it like this:

class FooY(MyMixin,ProvideMethodX,BaseB)

Finally (and probably least confusingly) you could use separate mixins
for bases that define method_x and those that don't.


Carl Banks

I was trying to adapt Ryles' code into my existing example. I did what
I thought Ryles did, but it didn't seem to work. I also tried flipping
the order of the parents, as you suggested, but that didn't seem to
change anything; it gives the same error.

Also, there is no fear that one of the bases will not define method_x.
The current API *requires* that all bases (MyMixin, BaseA, etc.)
define a method_x, so if it does not exist for a base class, the
developer isn't following the instructions. ;)
 
T

The Music Guy

I should also mention--and I should have realized this much
sooner--that each of the BaseN classes are themselves each going to
have at least one common base which will define method_x, so each
BaseN will be calling that if it defines its own method_x. Again,
sorry I didn't mention that sooner. For some reason it didn't occur to
me that it would be important. I feel dumb now... :p

Here is the updated current example:

class CommonBase(object):
def method_x(self, a, b c):
... no call to superclass' method_x is needed here ...

class MyMixin(object):
def method_x(self, a, b, c):
get_other_base(self).method_x(self, a, b, c)
...

class BaseA(CommonBase):
def method_x(self, a, b, c):
super(BaseA, self).method_x(a, b, c)
...

class BaseB(CommonBaset):
...

class BaseC(CommonBase):
def method_x(self, a, b, c):
super(BaseC, self).method_x(a, b, c)
...

class FooX(MyMixin, BaseA):
...

class FooY(MyMxin, BaseB):
...

class FooZ(MyMixin, BaseC):
...
 
R

ryles

I should also mention--and I should have realized this much
sooner--that each of the BaseN classes are themselves each going to
have at least one common base which will define method_x, so each
BaseN will be calling that if it defines its own method_x. Again,
sorry I didn't mention that sooner. For some reason it didn't occur to
me that it would be important. I feel dumb now... :p

Here is the updated current example:

class CommonBase(object):
    def method_x(self, a, b c):
        ... no call to superclass' method_x is needed here ...

class MyMixin(object):
   def method_x(self, a, b, c):
       get_other_base(self).method_x(self, a, b, c)
       ...

class BaseA(CommonBase):
   def method_x(self, a, b, c):
       super(BaseA, self).method_x(a, b, c)
       ...

class BaseB(CommonBaset):
   ...

class BaseC(CommonBase):
   def method_x(self, a, b, c):
       super(BaseC, self).method_x(a, b, c)
       ...

class FooX(MyMixin, BaseA):
   ...

class FooY(MyMxin, BaseB):
   ...

class FooZ(MyMixin, BaseC):
   ...

OK, how about this?

class CommonBase(object):
def method_x(self, a, b, c):
print "CommonBase"

class MyMixin(object):
def method_x(self, a, b, c):
print "MyMixin",
super(MyMixin, self).method_x(a, b, c)

class BaseA(CommonBase):
def method_x(self, a, b, c):
print "BaseA",
super(BaseA, self).method_x(a, b, c)

class BaseB(CommonBase):
def method_x(self, a, b, c):
print "BaseB",
super(BaseB, self).method_x(a, b, c)

class BaseC(CommonBase):
def method_x(self, a, b, c):
print "BaseC",
super(BaseC, self).method_x(a, b, c)

class FooX(MyMixin, BaseA):
def method_x(self, a, b, c):
print "FooX",
super(FooX, self).method_x(a, b, c)

class FooY(MyMixin, BaseB):
def method_x(self, a, b, c):
print "FooY",
super(FooY, self).method_x(a, b, c)

class FooZ(MyMixin, BaseC):
# Supposing this class doesn't require a specialized method_x.
pass


FooX().method_x(1, 2, 3) # Prints 'FooX MyMixin BaseA CommonBase'
FooY().method_x(1, 2, 3) # Prints 'FooY MyMixin BaseB CommonBase
FooZ().method_x(1, 2, 3) # Prints 'MyMixin BaseC CommonBase'

---

Each method_x implementation uses super() to ensure that the next base
class method_x is called. The exception is CommonBase, which is
presumed to be the hierarchy root.

http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
http://www.python.org/download/releases/2.3/mro
 
C

Carl Banks

I should also mention--and I should have realized this much
sooner--that each of the BaseN classes are themselves each going to
have at least one common base which will define method_x, so each
BaseN will be calling that if it defines its own method_x. Again,
sorry I didn't mention that sooner. For some reason it didn't occur to
me that it would be important. I feel dumb now... :p

Here is the updated current example:

class CommonBase(object):
    def method_x(self, a, b c):
        ... no call to superclass' method_x is needed here ...

class MyMixin(object):
   def method_x(self, a, b, c):
       get_other_base(self).method_x(self, a, b, c)
       ...

What is get_other_base? Just use a regular super call here,
get_other_base and hacks like that are what gets you into trouble.

You seem to be overthinking this. You don't need to. Just use super
() in MyMixin, and in all the other classes, consistently, and mind
the order of the bases.

And if I were you I wouldn't keep making updates to a "current
example" because first you do questionable things define a
get_other_base method, then you try to apply my advice without
reverting to the original form you posted. Well of course you're
going to have issues if you do that. Instead, start from scratch, and
try to get a small example working, using your orginial post and my
original suggestion. Once that works then try to apply it to your
working example.


Carl Banks
 
T

The Music Guy

On Sep 8, 10:47 pm, The Music Guy <[email protected]> wrote:
What is get_other_base?  Just use a regular super call here,
get_other_base and hacks like that are what gets you into trouble.

You seem to be overthinking this.  You don't need to.  Just use super
() in MyMixin, and in all the other classes, consistently, and mind
the order of the bases.

And if I were you I wouldn't keep making updates to a "current
example" because first you do questionable things define a
get_other_base method, then you try to apply my advice without
reverting to the original form you posted.  Well of course you're
going to have issues if you do that.  Instead, start from scratch, and
try to get a small example working, using your orginial post and my
original suggestion.  Once that works then try to apply it to your
working example.

get_other_base() is supposed to return the other superclass (base)
that is being used in conjunction with the current superclass. So if
FooX inherits from MyMixin and BaseB, and get_other_base(self) is
called from a method defined in MyMixin, BaseB is returned. In any
case, that function is merely psuedocode, not an actual part of the
implementation. Its purpose in my example was merely to show the
effect I was trying to acheive. I used it because I'm having trouble
understanding exactly how the super() function works; it always seems
to do something I didn't expect, or not do something that I did
expect. (I say this after having read the online documentation, btw.)

Anyway, Ryles' last suggestion helped a lot. Thanks, Ryles. (And Carl
and Scott, too, of course.) Here's the code I'm going with:

def main():

basea = BaseA()
baseb = BaseB()
basec = BaseC()

foox = FooX()
fooy = FooY()
fooz = FooZ()

basea.method_x("I", "Am", "BaseA!");
print
baseb.method_x("One", "Two", "Three");
print
basec.method_x("Ay", "Bee", "See");
print

print

foox.method_x("I", "AM", "X")
print
fooy.method_x("ESTOY", "Y", "!")
print
fooz.method_x(100, 200, 300)


class MyMixin(object):

def method_x(self, a, b, c):
super(MyMixin, self).method_x(a, b, c)
print "MyMixin.method_x(%s, %s, %s, %s)" % (repr(self),
repr(a), repr(b), repr(c))

class CommonBase(object):

def method_x(self, a, b, c):
print "CommonBase.method_x(%s, %s, %s, %s)" % (repr(self),
repr(a), repr(b), repr(c))

class BaseA(CommonBase):

def method_x(self, a, b, c):
super(BaseA, self).method_x(a, b, c)
print "BaseA.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class BaseB(CommonBase):

def method_x(self, a, b, c):
super(BaseB, self).method_x(a, b, c)
print "BaseB.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

class BaseC(CommonBase):

pass

class FooX(MyMixin, BaseA):

def method_x(self, a, b, c):
super(FooX, self).method_x(a, b, c)
print "FooX.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

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.method_x(%s, %s, %s, %s)" % (repr(self), repr(a),
repr(b), repr(c))

if __name__ == '__main__':
main()


Output:


CommonBase.method_x(<__main__.BaseA object at 0xb7d0d42c>, 'I', 'Am', 'BaseA!')
BaseA.method_x(<__main__.BaseA object at 0xb7d0d42c>, 'I', 'Am', 'BaseA!')

CommonBase.method_x(<__main__.BaseB object at 0xb7d0d44c>, 'One',
'Two', 'Three')
BaseB.method_x(<__main__.BaseB object at 0xb7d0d44c>, 'One', 'Two', 'Three')

CommonBase.method_x(<__main__.BaseC object at 0xb7d0d46c>, 'Ay', 'Bee', 'See')


CommonBase.method_x(<__main__.FooX object at 0xb7d0d48c>, 'I', 'AM', 'X')
BaseA.method_x(<__main__.FooX object at 0xb7d0d48c>, 'I', 'AM', 'X')
MyMixin.method_x(<__main__.FooX object at 0xb7d0d48c>, 'I', 'AM', 'X')
FooX.method_x(<__main__.FooX object at 0xb7d0d48c>, 'I', 'AM', 'X')

CommonBase.method_x(<__main__.FooY object at 0xb7d0d4ac>, 'ESTOY', 'Y', '!')
BaseB.method_x(<__main__.FooY object at 0xb7d0d4ac>, 'ESTOY', 'Y', '!')
MyMixin.method_x(<__main__.FooY object at 0xb7d0d4ac>, 'ESTOY', 'Y', '!')

CommonBase.method_x(<__main__.FooZ object at 0xb7d0d4cc>, 100, 200, 300)
MyMixin.method_x(<__main__.FooZ object at 0xb7d0d4cc>, 100, 200, 300)
FooZ.method_x(<__main__.FooZ object at 0xb7d0d4cc>, 100, 200, 300)


You may notice that MyMixin calls the super's method before doing
anything of its own. This is intentional--MyMixin's job cannot be done
correctly until the other bases have done their part.
 
T

The Music Guy

Btw, Carl, please forgive me if I frustrate you, because I'm trying my
best not to. I'm trying to keep track of what I did and what you did
and what Ryles and Scott did, while at the same time trying to keep a
firm grasp of exactly what it is I'm trying to acheive. Besides that,
I'm not a professional programmer--just a hobbyist. And of course,
there's all the other IRL things I'm trying to deal with
simultaneously with this...hopefully you can see how I would make
mistakes.
 
C

Carl Banks

get_other_base() is supposed to return the other superclass (base)
that is being used in conjunction with the current superclass. So if
FooX inherits from MyMixin and BaseB, and get_other_base(self) is
called from a method defined in MyMixin, BaseB is returned. In any
case, that function is merely psuedocode, not an actual part of the
implementation.

Ah, but see unless you tell us that, we will not know and will give
you inappropriate advice. Moral of the story: post the code that you
actually ran. And if you take anyone's advice, apply it to the actual
code they were advising you on.

Its purpose in my example was merely to show the
effect I was trying to acheive. I used it because I'm having trouble
understanding exactly how the super() function works; it always seems
to do something I didn't expect, or not do something that I did
expect. (I say this after having read the online documentation, btw.)

super() is unfortunately misnamed after the analogous feature of Java;
the analogy breaks down under multiple inheritance. Think of super as
more like "call next method", where the next method could be from a
base class or a sister class. However it always works left to right
in the list of bases, which is why it was important for MyMixin to be
listed first.

Anyway, Ryles' last suggestion helped a lot. Thanks, Ryles. (And Carl
and Scott, too, of course.) Here's the code I'm going with:

I'm glad it worked out.


Carl Banks
 
R

ryles

Btw, Carl, please forgive me if I frustrate you, because I'm trying my
best not to. I'm trying to keep track of what I did and what you did
and what Ryles and Scott did, while at the same time trying to keep a
firm grasp of exactly what it is I'm trying to acheive. Besides that,
I'm not a professional programmer--just a hobbyist. And of course,
there's all the other IRL things I'm trying to deal with
simultaneously with this...hopefully you can see how I would make
mistakes.

Glad to see your project is afoot. Multiple inheritance is tricky
stuff. Try to keep class hierarchies simple and prefer composition
when feasible.

Have a look at the links I suggested in my previous post, particularly
the new-style classes paper. It's tough reading but if you get through
it you'll gain a lot.

And don't worry about Carl, he's a Calvin Klein model and, as you
know, sometimes they can get a bit moody ;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top