Home > Mobile >  How do I make sure a super method is called on child classes method on Python?
How do I make sure a super method is called on child classes method on Python?

Time:01-25

How to make it sure in a parent class method that super is called in its children methods which override the parent method? I found this question in SO for other languages except for Python.

CodePudding user response:

In Python, there is no way to enforce that a child class method must call the parent class method when it overrides it. However, a common convention is to include a call to the parent class method at the beginning of the child class method, using the super() function. This allows the parent class method to perform any necessary actions before the child class method does its own processing.

Example:

class Parent:
    def method(self):
        print("Parent method called")

class Child(Parent):
    def method(self):
        super().method()  # call the parent method
        print("Child method called")

child = Child()
child.method()

This will output:

Parent method called
Child method called

It is a good practice to call the parent method in child class method, However it is not enforced.

CodePudding user response:

To the best of my knowledge, there is no way to enforce that a child class method must call the parent class method when it overrides it.

Nevertheless, a workaround could be to use a decorator calling the overriden method.

Based on this answer,

import inspect 


def get_class_that_defined_method(meth):
    if inspect.ismethod(meth):
        for cls in inspect.getmro(meth.__self__.__class__):
            if meth.__name__ in cls.__dict__:
                return cls
    if inspect.isfunction(meth):
        return getattr(inspect.getmodule(meth),
                       meth.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0],
                       None)
    return None  


def override(func):
    def wrapper(*args, **kwargs):
        cls = get_class_that_defined_method(func)
        if hasattr(cls.__base__, func.__name__):
            getattr(cls.__base__, func.__name__)(*args, **kwargs)
        return func(*args, **kwargs)

    return wrapper

This can be used as follows.

class A:
    def method(self):
        print("call A.method")


class B(A):
    @override
    def method(self):
        print("call B.method")


B().method()
# call A.method
# call B.method

CodePudding user response:

This can be achieved by using decorator and metaclass

import inspect


def my_decorator(func):
    def wrapper(self, *args, **kwargs):
        has_super = False
        for line in inspect.getsource(func).split('\n'):
            if line.strip().startswith("super"):
                has_super = True
                break
        if not has_super:
            super_method = getattr(super(self.__class__, self), func.__name__, None)
            if super_method:  
                super_method(*args, **kwargs)
        func(self, *args, **kwargs)
    return wrapper


class MyMeta(type):

    def __new__(cls, name, bases, local):
        for attr in local:
            if callable(local[attr]):
                local[attr] = my_decorator(local[attr])
        return super().__new__(cls, name, bases, local)


class BaseClass:
    
    def __init__(self):
        print("BaseClass __init__")

    def method1(self):
        print("BaseClass method1")

    def method2(self):
        print("BaseClass method2")


class Child(BaseClass, metaclass=MyMeta):

    def __init__(self):
        print("Child __init___")

    def method1(self):
        print("Child method1")

    def method2(self):
        print("Child method2")
        super().method2()


if __name__=="__main__":
    a = Child()
    a.method1()
    a.method2()

Output

BaseClass __init__
Child __init___
BaseClass method1
Child method1
Child method2
BaseClass method2

CodePudding user response:

While you can not force the call to the superclass, what you can do is force the call of one function when ANOTHER one is overridden:

class Parent:
    def other_method(self):
        # code that would be in overridden function should be placed here
        print("Hello from parent!")

    def method(self):
        pass

    def run_method(self):
        self.other_method()
        self.method()


class Child(Parent):
    def method(self):
        print("Hello from child!")


myChild= Child()
# calling method() here will not have the desired effect, one needs to call run_method()
myChild.run_method()

This will produce following output:

Hello from parent!
Hello from child!

The child method is overriding method() without calling super().

  • Related