Using super to reuse method with inheritance in Python

Posted on 2025-06-09 in Trucs et astuces

If you use Python, you probably already know of super to call a method from the base class. You probably also know you can use a function as a method if it takes the instance as 1st parameter. What you cannot do, is call super().my_method() directly in such a function. To do that, you must use the parameters of super to call it just like it was done in Python 2, so with super(type(instance), instance).

To give more details (and testable code) to clarify what’s happening, please read below.

Let’s start by defining our base class and its methods:

class BaseClass:
    def __init__(self):
        self.base_attr = "Test"

    def base_method(self, a_parameter: int) -> int:
        print("Base class base method")
        return a_parameter

    def shared_method(self, a_parameter: int) -> int:
        raise NotImplementedError

base_method we will want to call, a base_attr to use and the shared_method that must be implemented in the sub-classes. That’s shared_method that will be defined with a function in our child classes.

Let’s try with a basic usage, where we only use a function as a method:

def _shared_no_super(instance: BaseClass, a_parameter: int) -> int:
    value = instance.base_method(a_parameter)
    print("Inside shared method no super", value, instance.base_attr)
    return value

class A(BaseClass):
    def base_method(self, a_parameter: int) -> int:
        print("Inside A base method")
        return super().base_method(a_parameter) + 1

    shared_method_no_super = _shared_no_super

a = A()
a.shared_method_no_super(0)

As expected, a.shared_method_no_super(0) will call the code in _shared_no_super and will call the method on our a instance and then on the base method. The output will be:

Inside A base method
Base class base method
Inside shared method no super 1 Test

Now let’s try to use super in the function to bypass the method on the class and go directly to the base class. The naive approach is to use super() directly like in the class. Let’s try that:

def _invalid_shared(instance: BaseClass, a_parameter: int) -> int:
    value = super().base_method(a_parameter)
    raise Exception("This should never be printed")

class Invalid:
    def base_method(self, a_parameter: int) -> int:
        raise Exception("This should never be reached")

    shared_method = _invalid_shared

instance = Invalid()
instance.shared_method(0)

This will result in a RuntimeError:

Traceback (most recent call last):
  File "main.py", line 47, in <module>
    instance.shared_method(10)
  File "main.py", line 34, in _invalid_shared
    value = super().base_method(a_parameter)
RuntimeError: super(): __class__ cell not found

It turns out that Python 3 does some magic behind the scene to make a simple super() work and do the right thing. Since here it’s not called with a class context, this magic cannot happen. Hence the error.

If like me you used Python 2, you may remember how super had to be used: super(MyClass, instance). The goal of these parameters was to help Python find the proper class on which to start looking for the super method. This is still possible in Python 3, both for compatibility reason and (I suppose) to allow more advanced uses of super.

Let’s see that in action:

def _shared_method(instance: BaseClass, a_parameter: int) -> int:
    value = super(type(instance), instance).base_method(a_parameter)
    print("Inside shared method", value)
    return value


class B(BaseClass):
    def base_method(self, a_parameter: int) -> int:
        print("Inside B base method")
        return a_parameter * 2

    shared_method = _shared_method


b = B()
b.shared_method(1)

Now this works as expected and prints, as expected:

Base class base method
Inside shared method 1

Of course, it also works if used as a fonction directly:

_shared_method(b, 2)

Will print:

Base class base method
Inside shared method 2

Fun trick isn’t it? I don’t think you’ll need it often since most of the time code between classes will be shared in a base class (or a mixin). But I still think it’s good to know for the weird and niche cases when you need to share code between classes, use super but just cannot rely on inheritance.