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()
.