views:

51

answers:

4

With the following code sample, can super be used, or C has to call A.foo and B.foo explicitly?

class A(object):
    def foo(self):
        print 'A.foo()'

class B(object):
    def foo(self):
        print 'B.foo()'

class C(A, B):
    def foo(self):
        print 'C.foo()'
        A.foo(self)
        B.foo(self)
+1  A: 

If you added the following:

class A(object):
    def foo(self):
        print 'A.foo()'

class B(object):
    def foo(self):
        print 'B.foo()'

class C(A, B):
    def foo(self):
        super(C, self).foo()
        print 'C.foo()'
        A.foo(self)
        B.foo(self)


c = C()
c.foo()

Then super(C, self).foo() refers to A.foo

The output is

A.foo()
C.foo()
A.foo()
B.foo()

[Edit: Include additional information and links]

pyfunc
Yes, that was my question. So super is not meant for this case?
sayap
@sayap : I have edited my answer to include links that are relevant for understanding how super works. It works but it will find A.foo first and if A.foo was not there than B.foo would have been looked into.
pyfunc
Why would I want to have A.foo() called twice?
sayap
@sayap: This is just an example. That shows that super uses MRO to find the foo to be called. This could be search for A.foo then B.foo. If you have a requirement that both A.foo and B.foo should be called, call the explicitly or else you expect one of the base classes to implement foo then call super(..).foo. In this case, you just expect one of them to implement foo.
pyfunc
+2  A: 

super() will only ever resolve a single class type for a given method, so if you're inheriting from multiple classes and want to call the method in both of them, you'll need to do it explicitly.

Amber
Thanks for the answer. Now I don't feel dirty anymore for not using super().
sayap
+2  A: 

Super will call the foo method on the "first" super class. This is based on the Method Resolution Order (__mro__) for the class C.

>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)
>>> 

Therefore if you call super(C, self).foo(), A.foo is called. If you change the inheritance order to class C(B, A): then this is reversed. The __mro__ now looks like:

>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
>>> 

If you call super(C, self).foo() after making this change, B.foo() will get called.

Manoj Govindan
yay for introspection
bronzebeard
+2  A: 

super is indeed intended for this situation, but it only works if you use it consistently. If the base classes don't also all use super it won't work, and unless the method is in object you have to use something like a common base class to terminate the chain of super calls.

class FooBase(object):
    def foo(self): pass

class A(FooBase):
    def foo(self):
        super(A, self).foo()
        print 'A.foo()'

class B(FooBase):
    def foo(self):
        super(B, self).foo()
        print 'B.foo()'

class C(A, B):
    def foo(self):
        super(C, self).foo()
        print 'C.foo()'
Duncan