I was trying to perform an action before and after the execution of some methods in my class.
First I thought about using a decorator to extend the functionality, something like this:
def decorator(f):
def wrap(*args, **kwargs):
print("before")
f(*args, **kwargs)
print("after")
return wrap
class Foo(object):
@decorator
def do(self):
print("This is do")
a = Foo()
a.do()
This outputs what I want:
before
This is do
after
But then I realized that if I wanted to inherit from Foo
to extend do
that wouldn't work, at least the way I'm doing it:
class Bar(Foo):
def do(self):
super(Bar, self).do()
print("This is the new implementation of do")
b = Bar()
b.do()
This will be the output and it's not okay. "after" should be the last thing to be printed:
before
This is do
after
This is the new implementation of do
Maybe there is another way to decorate do
in Bar
so it does what I want but I don't know how (if there is I would like to know it) and tbh decorating do
every time doesn't seem like a good way to go.
So finally I came up with what I think is a nice solution. Defining __getattribute__
so it returns a wrapper for do
:
class Foo(object):
def __getattribute__(self, name):
attribute = super(Foo, self).__getattribute__(name)
if name == "do":
def wrap(*args, **kwargs):
print("before")
attribute(*args, **kwargs)
print("after")
return wrap
else:
return attribute
def do(self):
print("This is do")
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
def do(self):
super(Bar, self).do()
print("This is the new implementation of do")
a = Bar()
a.do()
Is this a good solution? Any downsides? I'm missing something that could create a problem in the future? Other workarounds?
Thx!!
CodePudding user response:
You can access the wrapped function in python3 with __wrapped__
, since it's a custom decorator you need a slight modification with the functools.wraps
decorator.
from functools import wraps
def decorator(f):
@wraps(f)
def wrap(*args, **kwargs):
print("before")
f(*args, **kwargs)
print("after")
return wrap
Reapply the decorator to the new do()
and strip it from the old one
class Bar(Foo):
@decorator
def do(self):
super(Bar, self).do.__wrapped__(super)
print("This is the new implementation of do")
you get:
before
This is do
This is the new implementation of do
after
CodePudding user response:
You could place the implementation of do() in a separate method that will be the one to override in subclasses and have the main do() method with pre/post processing only in the base class:
class Foo:
def _do(self):
# do your stuff
def do(self):
self.preProcessing()
self._do()
self.postProcessing()
class Bar(Foo):
def _do(self):
super()._do()
# do more stuff