Inheritance: changing the signature of child methods
I am trying to figure out what is the best practice in Python inheritance principles when there is a "bad idea" to change the method signature of a child.
Suppose we have a base class BaseClient
with a method already implemented create
(and some abstract ones) that is suitable for almost all "descendants" except one:
class BaseClient(object):
def __init__(self, connection=None):
pass
def create(self, entity_id, data=None):
pass
class ClientA(BaseClient):
pass
class ClientB(BaseClient):
pass
A single class ClientC
needs a different method implementation create
with a slightly different method signature
class ClientC(BaseClient):
....
def create(self, data):
pass
So the question is, how do you do it in a more "pythonic" way, taking into account best python practice? Of course we can use *args, **kwargs
other approaches **kwargs
in the parent (child) method as well, but I'm afraid my code is becoming less readable (self-documenting).
source to share
I'm not sure if there is a way like Pythonic, since you can do the same as in the question. Rather, I would say that this is more about OOP than about Python.
So I am assuming there are other methods implemented in BaseClient
besides create
that other children share (otherwise no point makes a ClientC
child BaseClient
). In your case, it looks like it ClientC
deviates from the rest by requiring a different method signature create
. Then it might be worth considering separating them?
For example, you can have root BaseClient
implement all the common methods except create
, and then add two more "base" children, for example:
class EntityBasedClient(BaseClient):
def create(self, entity_id, data=None):
pass
class DataBasedClient(BaseClient):
def create(self, data):
pass
So now you can inherit without breaking any rule:
class ClientA(EntityBasedClient):
pass
class ClientB(EntityBasedClient):
pass
class ClientC(DataBasedClient):
pass
Also, if the implementation of the two versions is create
fairly similar, you can avoid code duplication by adding a more general private method implemented in BaseClient
signed _create(self, entity_id=None, data=None)
, then call it with the appropriate arguments from within EntityBasedClient
and DataBasedClient
.
source to share
I would say just add the parameter back as a keyword with a default of None. Then raise a bug that explains that some input data was lost.
class ClientC(BaseClient):
....
def create(self,entity_id=None, data):
if entity_id:
raise RedudantInformationError("Value for entity_id does nothing")
pass
This way, whenever a programmer tries to process child C like other children, he will get a warning that reminds him, but nevertheless, he can easily follow step by step using try syntax.
source to share