Here's an interesting problem I came upon while playing around with classes.
Let's say I have a class A, and I have a method that takes an object of class A and returns an instance of itself. That'll look something like this:
So that's cute and all, but what if I want to create a class B that inherits all the methods of class A?
Turns out when I call method() of an object B, it'll return an instance of an object of class A, not B. That's pretty obvious if you look at the code for method(). So what can be done to method() in A so that I can have an instance of B returned when I use method() on B?
Well, after some googling I found a suggestion, to replace the return statement of method() with:
Let's say I have a class A, and I have a method that takes an object of class A and returns an instance of itself. That'll look something like this:
class A:
def method(self: 'A') -> 'A':
return A()
So that's cute and all, but what if I want to create a class B that inherits all the methods of class A?
class B(A):
pass
Turns out when I call method() of an object B, it'll return an instance of an object of class A, not B. That's pretty obvious if you look at the code for method(). So what can be done to method() in A so that I can have an instance of B returned when I use method() on B?
Well, after some googling I found a suggestion, to replace the return statement of method() with:
return self.__init__()
Unfortunately, that won't work. Because all I'm doing is initialising self again. Something I would've already done before I used method(). What I need is to create a new instance, not re-initialise an existing instance.
There is also another suggestion, again changing the return statement to:
return self.class.__name__.__init__()
This is still wrong. The object self.class.__name__() is a string containing the name of the class. But wait, that's sort of close to what we want right? Just that we don't a string, but an instance of the class itself.
return self.class.__init__()
There we go. This beauty here will work. It calls on the class of self and initialises another object of the same class. And this does support the inheritance by B. The fact is, when it comes to inheritance, there should be an easy way to avoid replicating code from a superclass to a subclass. If not, then what's the point of inheritance?
Honestly, we could have worked with self.class.__name__(). We could've somehow extracted the class name from the string and use it to initialise a new instance.
And honestly, we could've just copied the entire method(), pasted it in B() and just change the return statement to B() instead of A().
So why didn't we? The simple answer is that I'm just too goddamn lazy. It's too tedious and complicated to workaround with those simple solutions. I'd rather take the time to search for an elegant solution that will straight up work exactly as I want it to.
I think that's one of the most important lessons we can take away as students of computer science. That it isn't about just getting your code to work; it's about how it works.