Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force child class to call parent method when overriding it

Tags:

python

class

I am curious whether there is a way in Python to force (from the Parent class) for a parent method to be called from a child class when it is being overridden.

Example:

class Parent(object):      def __init__(self):         self.someValue=1      def start(self):         ''' Force method to be called'''         self.someValue+=1 

The correct implementation of what I wanted my child Child class to do would be:

class ChildCorrect(Parent):      def start(self):         Parent.start(self)         self.someValue+=1 

However, is there a way to force developers that develop Child classes to specifically call(not just override) the 'start' method defined in the Parent class in case they forget to call it:

class ChildIncorrect(Parent):      def start(self):         '''Parent method not called, but correctly overridden'''         self.someValue+=1 

Also, if this is not considered best practice, what would be an alternative?

like image 309
Spektor Avatar asked Dec 11 '15 13:12

Spektor


People also ask

Can a child class call parent class method?

If the child class constructor does not call super , the parent's constructor with no arguments will be implicitly called. If parent class implements a constructor with arguments and has no a constructor with no arguments, then the child constructors must explicitly call a parents constructor.

Can child class override the properties of parent class in Python?

If you add a method in the child class with the same name as a function in the parent class, the inheritance of the parent method will be overridden.

Can child class access parent methods?

The reference holding the child class object reference will not be able to access the members (functions or variables) of the child class. This is because the parent reference variable can only access fields that are in the parent class.


2 Answers

How (not) to do it

No, there is no safe way to force users to call super. Let us go over a few options which would reach that or a similar goal and discuss why it’s a bad idea. In the next section, I will also discuss what the sensible (with respect to the Python community) way to deal with the situation is.

  1. A metaclass could check, at the time the subclass is defined, whether a method overriding the target method (= has the same name as the target method) calls super with the appropriate arguments.

    This requires deeply implementation-specific behaviour, such as using the dis module of CPython. There are no circumstances where I could imagine this to be a good idea—it is dependent on the specific version of CPython and on the fact that you are using CPython at all.

  2. A metaclass could co-operate with the baseclass. In this scenario, the baseclass notifies the metaclass when the inherited method is called and the metaclass wraps any overriding methods with a piece of guard code.

    The guard code tests whether the inherited method got called during the execution of the overriding method. This has the downside that a separate notification channel for each method which needs this "feature" is required, and in addition thread-safety and re-entrance would be a concern. Also, the overriding method has finished execution at the point you notice that it has not called the inherited method, which may be bad depending on your scenario.

    It also opens the question: What to do if the overriding method has not called the inherited method? Raising an exception might be unexpected by the code using the method (or, even worse, it might assume that the method had no effect at all, which is not true).

    Also the late feedback the developer overriding the class (if they get that feedback at all!) is bad.

  3. A metaclass could generate a piece of guard code for every overriden method which calls the inherited method automatically before or after the overriding method has executed.

    The downside here is developers will not be expecting that the inherited method is being called automatically and have no way to stop that call (e.g. if a precondition special to the subclass is not met) or control when in their own overriding method the inherited method is called.

    This violates a good bunch of sensible principles when coding python (avoid surprises, explicit is better than implicit, and possibly more).

  4. A combination of point 2 and 3. Using the co-operation of base- and metaclass from point 2, the guard code from point 3 could be extended to automatically call super iff the overriding method has not called super themselves.

    Again, this is unexpected, but it resolves the issues with duplicate super calls and how to handle a method which does not call super.

    However, there are still remaining problems. While thread-safety could be fixed with thread-locals, there is still no way for an overriding method to abort the call to super when a precondition is not met, except by raising an exception which may not be desirable in all cases. Also, super can only be called automatically after the overriding method, not before, which, again, in some scenarios is undesirable.

Also, none of this help against re-binding the attribute during the lifetime of the object and class, although this can be helped by using descriptors and/or extending the metaclass to take care of it.

Why not to do it

Also, if this is not considered best practice, what would be an alternative?

A common best-practice with Python is to assume that you are among consenting adults. That means, that noone is actively trying to do nasty things to your code, unless you allow them to. In such an ecosystem, it would make sense to tack a .. warning:: in the documentation of the method or class, so that anyone inheriting from that class knows what they have to do.

Also, calling the super method at an appropriate point makes sense in so many contexts that developers using the base class will consider it anyways and only forget about it accidentally. In such a case, using the metaclass from the third point above would not help either—users would have to remember not to call super, which might be an issue by itself, especially for experienced programmers.

It also violates the principle of least surprise and "explicit is better than implicit" (noone expects the inherited method to be called implicitly!). This, again, would have to be documented well, in which case you can also resort to just not have super be called automatically and just document that it makes even more sense than usual to call the inherited method.

like image 79
Jonas Schäfer Avatar answered Oct 14 '22 03:10

Jonas Schäfer


If the class hierarchy is under your control, you can use what the Gang of Four (Gamma, et al) Design Patterns book calls the Template Method Pattern:

class MyBase:    def MyMethod(self):       # place any code that must always be called here...       print "Base class pre-code"        # call an internal method that contains the subclass-specific code       self._DoMyMethod()        # ...and then any additional code that should always be performed       # here.       print "base class post-code!"     def _DoMyMethod(self):       print "BASE!"   class MyDerived(MyBase):    def _DoMyMethod(self):       print "DERIVED!"   b = MyBase() d = MyDerived()  b.MyMethod() d.MyMethod() 

outputs:

Base class pre-code BASE! base class post-code! Base class pre-code DERIVED! base class post-code! 
like image 27
bgporter Avatar answered Oct 14 '22 04:10

bgporter