S
Steven D'Aprano
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.
-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.
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.
-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.
-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()
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.
-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.
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.
-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.
-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()