Reputation: 322
What I'm trying to achieve is to wrap specific method calls of a class with a method of its subclass. I could do this one by one, like this:
class Subclass(ParentClass):
def _handle_response(self, data, _suppress=None):
# process data
return data
def foo(self, *a, _suppress=None, **kw):
return self._handle_response(super().foo(*a, **kw), _suppress=_suppress)
def bar(self, *a, _suppress=None, **kw):
return self._handle_response(super().bar(*a, **kw), _suppress=_suppress)
But since there are ~20 methods that need to be wrapped this way, it strikes me as quite redundant.
Another way I've come up with is to use __getattribute__
like this:
class Subclass(ParentClass):
def _handle_response(self, data, _suppress=None):
# process data
return data
def __getattribute__(self, attr):
if attr in {"foo", "bar"}:
def wrapped(_self, *a, _suppress=None, **kw):
return self._handle_response(
getattr(ParentClass, attr)(_self, *a, **kw),
_suppress=_suppress
)
return wrapped
return super().__getattribute__(attr)
This works but I'm not really happy with the fact that each time a method is called like this, a new wrapper function is created. This a lot of unnecessary overhead that I'd like to avoid. I could cache these, but again, this seems to be not a very elegant solution.
Anyone has an idea on how to approach this?
Upvotes: 1
Views: 222
Reputation: 322
For anyone interested, this is how I ended up solving the problem based on zxzak's answer.
class Subclass(ParentClass):
def __new__(cls, *args, **kwargs):
for method in ("foo", "bar", ...):
def make_wrapper(m):
def wrap(self, *a, _suppress=None, **kw):
return self._handle_response(getattr(super(type(self), self), m)(*a, **kw), _suppress=_suppress)
return wrap
setattr(cls, method, make_wrapper(m=method))
return super().__new__(cls, *args, **kwargs)
Upvotes: 0
Reputation: 9446
Classes are created during runtime so you can still modify them after the declaration and add the methods you want.
class ParentClass():
def foo(self, *a, _suppress=None, **kw):
return "foo"
def bar(self, *a, _suppress=None, **kw):
return "bar"
class Subclass(ParentClass):
def _handle_response(self, data, _suppress=None):
return data + " plus"
methods_to_wrap = ["foo", "bar"]
for method in methods_to_wrap:
def make_wrapper(m):
def wrap(self, *a, _suppress=None, **kw):
return self._handle_response(getattr(super(type(self), self), m)(*a, **kw), _suppress=_suppress)
return wrap
setattr(Subclass, method, make_wrapper(m=method))
o = Subclass()
print(o.foo())
print(o.bar())
prints
foo plus
bar plus
The reason I am using the make_wrapper
function is to avoid late binding. The rest should be self-explanatory.
Upvotes: 1