I am not a very great or experienced Python coder, and I'm wondering if there's an easy way to do what I need to do. But I haven't been able to find anything on my own because I'm not sure how to phrase the question properly.
The situation I have is more or less as follows:
class Insect:
def __init__(self, arg1):
self.arg1 = arg1
def insect_method(self):
print('do insect stuff')
class Caterpillar(Insect):
def __init__(self, arg1):
super().__init__(arg1)
def caterpillar_method(self):
print('do caterpillar stuff')
class Butterfly(Insect):
def __init__(self, arg1, arg2):
super().__init__(arg1)
self.arg2 = arg2
def butterfly_method(self):
print('do butterfly stuff')
I'm not actually simulating caterpillars and butterflies, but it's a similar process so I thought it might help make things clearer.
I have a super class 'Insect' which is never created by itself but exists so subclasses can inherit its methods. Most of the time, I am just spawning the subclasses directly and that's it. But I have a special case where the Caterpillar subclass should become an instance of the Butterfly subclass.
I have searched, and been convinced that changing __class__
is probably not what I want to do in the long run. However, I am also not sure if there's an elegant way to do this otherwise?
The only technique I can think of at the moment is to add in a bunch of if
statements like this:
class Insect:
def __init__(self, arg1):
self.arg1 = arg1
def insect_method(self):
print('do insect stuff')
class Caterpillar(Insect):
def __init__(self, arg1):
super().__init__(arg1)
def caterpillar_method(self):
print('do caterpillar stuff')
class Butterfly(Insect):
def __init__(self, arg1, arg2, caterpillar=None):
if not caterpillar:
super().__init__(arg1)
else:
self.arg1 = caterpillar.arg1
self.arg2 = arg2
def butterfly_method(self):
print('do butterfly stuff')
This would work if the actual code was as simple as this, but it's not, so I'd end up having to make a kind of double of the Insect.__init__
method in the Butterfly.__init__
method and then would have to make sure to constantly update both methods whenever changing anything in the Insect.__init__
method.
Is there an elegant Pythonic method to have this kind of functionality without needing to create a very convoluted __init__
method for the Butterfly class?
Many thanks in advance!!!
Instead of putting this logic in your __init__
, I'd maybe keep the __init__
simple and put the logic to create a Butterfly
from a Caterpillar
in a classmethod
:
class Butterfly(Insect):
def __init__(self, arg1, arg2):
super().__init__(arg1)
self.arg2 = arg2
@classmethod
def from_caterpillar(cls, caterpillar, arg2):
return cls(caterpillar.arg1, arg2)
Then you might have code that does something like:
bob = Caterpillar("Bob")
# metamorphose
bob = Butterfly.from_caterpillar(bob, "Small")
Note that similar to your original code, this doesn't mutate the original Caterpillar
object to make it a Butterfly
, it creates a new Butterfly
instance (and reassigns bob
to it).