Reputation: 1251
My apologies for what may be a basic question. I'm a C++ programmer who is relatively new to python.
I have a python class whose behavior depends significantly on one of its constructor arguments:
class MyClass():
def __init__(self, some_arg):
self.some_arg = some_arg
...
def abcd(self):
if self.some_arg == 1:
...
else:
...
def efgh(self):
if self.some_arg == 1:
...
else:
...
I would like to refactor this into two classes with different values of some_arg. Of course, the most straightforward thing would be to have two classes (perhaps with a common base class) and then have a factory function pick which one to instantiate. Something along the lines of:
def MyClassSomeArg1():
def __init__(self):
...
def abcd(self):
...
def efgh(self):
...
def MyClassSomeArgNot1():
def __init__(self):
...
def abcd(self):
...
def efgh(self):
...
def buildMyClass(some_arg):
if some_arg == 1:
return MyClassSomeArg1()
else:
return MyClassSomeArgNot1()
I'm sure that would work fine. The problem is that I don't want to change client code. Clients expect to instantiate an object of class "MyClass" with a constructor argument of some_arg. Is there a decent way to refactor this under the hood without changing client code?
I have tried using an implementation hierarchy: MyClassImpl as a base class with subclasses MyClassImplSomeArg1 and MyClassImplSomeArgNot1. MyClass itself then becomes mostly empty:
class MyClass():
def __init__(self, some_arg):
if some_arg == 1:
self._impl = MyClassImplSomeArg1()
else:
self._impl = MyClassImplSomeArgNone1()
def __getattr__(self, a):
# For performance, I could store this in self so it doesn't need to be looked up each time
return getattr(self._impl, a)
This basically works, but it doesn't seem to be the most straightforward thing. For one thing, magic methods like __str__ and __eq__ don't seem to get delegated through the __getattr__ mechanism, and I don't know why. It's not difficult to write delegation methods myself, though. Also, this confuses pydoc (it has no way of seeing the delegated attributes), and I'm not sure how to fix that.
Is there some sugar to make this delegation scheme work nicely? Or is delegation even the best way to handle an issue like this?
Thanks,
Upvotes: 0
Views: 1094
Reputation: 6344
You might override the __new__
function and return a subclass based on some_arg
. It's a common pattern for implementing factory
class MyClassImplSomeArg1:
pass
class MyClassImplSomeArgNone1:
pass
class MyClass:
def __new__(cls, some_arg):
if some_arg:
return MyClassImplSomeArg1()
else:
return MyClassImplSomeArgNone1()
assert isinstance(MyClass(some_arg=True), (MyClassImplSomeArg1,))
assert isinstance(MyClass(some_arg=False), (MyClassImplSomeArgNone1,))
Upvotes: 1