Alex Rivera | Logout

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

Asked 2011-11-01T17:16:42.973
14

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?

Edit
Report

2 Answers

17

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
answered 2011-11-01T18:12:17.207
2

One explicit solution without too much magic going on would be to maintain a list of prepare call-backs:

class BaseHandler(object):
    def __init__(self):
        self.prepare_callbacks = []
    def register_prepare_callback(self, callback):
        self.prepare_callbacks.append(callback)
    def prepare(self):
        # Do BaseHandler preparation
        for callback in self.prepare_callbacks:
            callback()

class MyHandler(BaseHandler):
    def __init__(self):
        BaseHandler.__init__(self)
        self.register_prepare_callback(self._prepare)
    def _prepare(self):
        # whatever
answered 2011-11-01T17:26:56.600

Your Answer