I would like to systematically wrap some overriden method of a base class.
I am using ABC
for the base class. I tried to wrap the @abstractmethod
, putting the annotation before or after, but it doesn’t work. As I understand it, the the whole wrapped method is overriden.
from functools import wraps
from abc import ABC, abstractmethod
def print_before(func):
@wraps(func)
def out(*args, **kwargs):
print('Hello')
return func(*args, **kwargs)
return out
class Base(ABC):
@print_before
@abstractmethod
def test(self):
pass
class Extend(Base):
def test(self):
print('World')
Here is what happens when we test:
Extend().test()
Result:
World
Desired:
Hello
World
I guess I’m not using the right method to get such behavior. What would be a nice pythonic way to run some code before and after an overriden method?
As you noticed, the decorator does not change overridden methods. You could decorate the method every time you create a subclass. You can even do it automatically with the magic method __init_subclass__
.
class Base(ABC):
...
def __init_subclass__(cls):
cls.test = print_before(cls.test)
But i would not recommend this approach. It will probably destroy the ABC
mechanisms and classes that inherit from Extend
are decorated twice if they don't override the method.
Here is a much easier approach. We define a concrete "public" method on Base
that calls an abstract "private" method. In the child classes we then have to implement the "private" method.
class Base(ABC):
def test(self):
# do something before
result = self._do_work()
# do something after
return result
@abstractmethod
def _do_work(self):
pass
class Extend(Base):
def _do_work(self):
# your implementation here
# use it like this:
e = Extend()
e.test()
Another advantage is that you can change the behaviour of the "wrapper" in a child class which would be difficult with the decorator.