Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I ensure that one of my class's methods is always called even if a subclass overrides it?

For example, I have a

class BaseHandler(object):
    def prepare(self):
        self.prepped = 1

I do not want everyone that subclasses BaseHandler and also wants to implement prepare to have to remember to call

super(SubBaseHandler, self).prepare()

Is there a way to ensure the superclass method is run even if the subclass also implements prepare?

like image 464
kortina Avatar asked Nov 01 '11 17:11

kortina


People also ask

When an overridden method is called from within a subclass it will always refer to the version of that method defined by the?

When an overridden method is called from within a subclass, it will always refer to the version of that method defined by the subclass. The version of the method defined by the superclass will be hidden.

When a superclass method has the same name as a subclass method it is often said that the superclass method overrides the subclass method?

When a subclass method has the same name as a superclass method, it is often said that the subclass method overrides the superclass method. When one object is a specialized version of another object, there is an " is a" relationship between them.

How do you invoke an overridden superclass method from a subclass?

It is what overriding for. The overridden method shall not be accessible from outside of the classes at all. But you can call it within the child class itself. to call a super class method from within a sub class you can use the super keyword.

How do you call an overridden method?

But, since you have overridden the parent method how can you still call it? You can use super. method() to force the parent's method to be called.


2 Answers

I have solved this problem using a metaclass.

Using a metaclass allows the implementer of the BaseHandler to be sure that all subclasses will call the superclasses prepare() with no adjustment to any existing code.

The metaclass looks for an implementation of prepare on both classes and then overwrites the subclass prepare with one that calls superclass.prepare followed by subclass.prepare.

class MetaHandler(type):
    def __new__(cls, name, bases, attrs):
        instance = type.__new__(cls, name, bases, attrs)
        super_instance = super(instance, instance)
        if hasattr(super_instance, 'prepare') and hasattr(instance, 'prepare'):
            super_prepare = getattr(super_instance, 'prepare')
            sub_prepare = getattr(instance, 'prepare')
            def new_prepare(self):
                super_prepare(self)
                sub_prepare(self)
            setattr(instance, 'prepare', new_prepare)
        return instance


class BaseHandler(object):
    __metaclass__ = MetaHandler
    def prepare(self):
        print 'BaseHandler.prepare'


class SubHandler(BaseHandler):
    def prepare(self):
        print 'SubHandler.prepare'

Using it looks like this:

>>> sh = SubHandler()
>>> sh.prepare()
BaseHandler.prepare
SubHandler.prepare
like image 88
j2labs Avatar answered Oct 03 '22 00:10

j2labs


Tell your developers to define prepare_hook instead of prepare, but tell the users to call prepare:

class BaseHandler(object):
    def prepare(self):
        self.prepped = 1
        self.prepare_hook()
    def prepare_hook(self):
        pass

class SubBaseHandler(BaseHandler):
    def prepare_hook(self):
        pass

foo = SubBaseHandler()
foo.prepare()

If you want more complex chaining of prepare calls from multiple subclasses, then your developers should really use super as that's what it was intended for.

like image 27
unutbu Avatar answered Oct 02 '22 23:10

unutbu