super() and multiple inheritance failure

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Fri Sep 25 22:36:18 EDT 2009


I don't understand why I'm getting the following behaviour when using 
super() with multiple inheritance. The following is a minimal example 
demonstrating the behaviour.

I have a diamond class hierarchy as follows:

 o
 |
 B
/ \
P  N
\ /
 M

where:
o = object
B = BaseClass
P = PClass
N = NClass
M = MyClass

Inside MyClass().method(n), I dispatch to either NClass.method() or 
PClass.method() depending on the value of the argument n. The correct 
class is called, but then the *other* class method is called as well. 
E.g. this is what I expect:

MyClass().method(2)
->  calls PClass.method
    -> calls BaseClass.method

but this is what I get:

MyClass().method(2)
->  calls PClass method
    ->  calls NClass.method
        -> calls BaseClass.method


and similarly for negative arguments, swapping PClass and NClass.

First off, is this the expected behaviour? I seems strange to me, can 
somebody explain why it is the nominally correct behaviour?

Secondly, how should I deal with this situation? Avoid super() 
altogether? Avoid multiple inheritance? Do something else?


Demonstration code follows, using doctest to demonstrate the failure and 
the call pattern.


### fail.py

import sys

class BaseClass(object):
    def method(self, n):
        """Return something. n must not be 0, 1 or -1.

        >>> instance = BaseClass()
        >>> instance.method(7)
        8
        >>> instance.method(-5)
        -4
        """
        assert int(n) == n and n not in (-1, 0, 1)
        return n + 1

class PClass(BaseClass):
    """Deal with positive n."""
    def method(self, n):
        """Return something. n must be strictly > 1.

        >>> instance = PClass()
        >>> instance.method(4)
        6
        """
        print >>sys.stderr, "Called from PClass"
        assert int(n) == n and n > 1
        return 1 + super(PClass, self).method(n)

class NClass(BaseClass):
    """Deal with negative n."""
    def method(self, n):
        """Return something. n must be strictly < -1.

        >>> instance = NClass()
        >>> instance.method(-4)
        -2
        """
        print >>sys.stderr, "Called from NClass"
        assert int(n) == n and n < -1
        return 1 + super(NClass, self).method(n)


class MyClass(PClass, NClass):
    def method(self, n):
        """Return something useful.

        >>> instance = MyClass()
        >>> instance.method(12)
        14
        >>> instance.method(-12)
        -10
        """
        #print >>sys.stderr, "Calling itoa with base=%d" % base
        if n > 0:
            print >>sys.stderr, "About to call PClass"
            parent = PClass
        else:
            print >>sys.stderr, "About to call NClass"
            parent = NClass
        return parent.method(self, n)


if __name__ == '__main__':
    import doctest
    doctest.testmod()



-- 
Steven



More information about the Python-list mailing list