Python: Decorar un método de clase que se pretende sobrescribir cuando se hereda

Digamos que tengo alguna clase base:

class Task: def run(self): #override this! 

Ahora, quiero que otros subclasificaran Tarea y anulen el método run ():

 class MyTask(Task): def run(self): #successful override! 

Sin embargo, el problema es que hay una lógica que debe tener lugar antes y después del método run () de cada clase que subclases Tarea.

Parece que una forma en que podría hacer esto sería definir otro método en la clase base que luego llama al método run (). Sin embargo, quería preguntar, ¿hay alguna manera de lograr esto con los decoradores? ¿Cuál sería la forma más pythonica de hacer esto?

Tal como se sugiere en los comentarios, probablemente sea mejor dejar que las subclases anulen un gancho en lugar de run sí mismo:

 class Task(object): def run(self): # before self.do_run() # after class MyTask(Task): def do_run(self): ... task = MyTask() task.run() 

Sin embargo, esta es una forma de hacerlo con un decorador de clase:

 def decorate_run(cls): run = getattr(cls, 'run') def new_run(self): print('before') run(self) print('after') setattr(cls, 'run', new_run) return cls class Task(object): pass @decorate_run class MyTask(Task): def run(self): pass task = MyTask() task.run() # prints: # before # after 

Otra forma sería utilizar una metaclase. La ventaja de usar una metaclase sería que las subclases no tendrían que ser decoradas. Task podría convertirse en una instancia de la metaclase, y luego todas las subclases de la Task heredarían la metaclase automáticamente.

 class MetaTask(type): def __init__(cls, name, bases, clsdict): if 'run' in clsdict: def new_run(self): print('before') clsdict['run'](self) print('after') setattr(cls, 'run', new_run) class Task(object, metaclass=MetaTask): # For Python2: remove metaclass=MetaTask above and uncomment below: # __metaclass__ = MetaTask pass class MyTask(Task): def run(self): #successful override! pass task = MyTask() task.run()