Consider the following class hierarchy:
A
|
_____________
| | |
Foo Bar Baz
The class A
defines a __call__
method, which Foo
, Bar
and Baz
implement. The classes also define various other methods I'm interested in. All three classes come from a third party library, so I do not want to change their implementation. However, in some cases at runtime, I would like to alter the implementation of Foo
's, Bar
's and Baz
' __call__
method. Right now, I did this by defining my own classes, which solely override the __call__
method:
Foo Bar Baz
| | |
Foo1 Bar1 Baz1
Then, depending on the situation, I instantiate the desired class to a common variable. I'm not satisfied with this solution, as it has the following drawbacks:
The additions I make to
__call__
are not necessarily uniquely determined by the class.Bar1
andBaz1
might share the implementation of__call__
(butBar
andBaz
differ in other aspects). So, as far as I understand, I need to repeat code.At runtime, instantiating the desired class requires m × n case distinctions (where m is the number of classes at the level of
Foo
,Bar
, andBaz
and n is the number of classes at the level ofFoo1
,Bar1
, andBaz1
). This will grow rapidly as m or n increase. Ideally, I would like the number of case distinction to be m + n.
I already had a look at https://python-patterns.guide/gang-of-four/composition-over-inheritance/#solution-3-the-decorator-pattern, at Cannot overwrite implementation for __call__ and at __call__ method of type class. However, these solutions do not fit perfectly to my case, as (1) I'm overriding a dunder method (2) of third party classes and (3) still would like to have access to the other class methods of Foo
, Bar
, and Baz
.
Is there an elegant why to achieve what I am looking for?
Edit: The change of __call__
at runtime should only affect the instance object, not the class or all objects of that class at once.
Edit 2 (requested by @enzo):
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
class A(metaclass=ABCMeta):
@abstractmethod
def __call__(self, x, y):
"""Docstring A"""
@abstractmethod
def method1(self, a, b):
"""Docstring method1"""
class Foo(A):
def __call__(self, x, y):
return x + y # indeed, no dependence on `self`
def method1(self, a, b):
# but this might depend on `self`
pass
class Bar(A):
def __call__(self, x, y):
return x * y # indeed, no dependence on `self`
def method1(self, a, b):
# but this might depend on `self`
pass
def method2(self, c, d):
# but this might depend on `self`
pass
class Baz(A):
def __call__(self, x, y):
return x ** y # indeed, no dependence on `self`
def method1(self, a, b):
# but this might depend on `self`
pass
def method2(self, c, d):
# but this might depend on `self`
pass