Suppose you have the following situation.
As you can see, makeSpeak is a procedure that accepts a common Animal object. In this case, Animal is very similar to the Java interface, since it contains only a pure virtual method. makeSpeak does not know the nature of the animals that it receives. It simply sends a “talk” signal and leaves the last binding to take care of which method to call: either Cat :: speak () or Dog :: speak (). This means that with regard to makeSpeak, knowing which subclass is actually passed does not matter.
But what about Python? Let's see the code for the same case in Python. Please note that I try to be as similar as possible to the C ++ case for a moment:
class Animal(object): def speak(self): raise NotImplementedError() class Dog(Animal): def speak(self): print "woff!" class Cat(Animal): def speak(self): print "meow" def makeSpeak(a): a.speak() d=Dog() c=Cat() makeSpeak(d) makeSpeak(c)
Now in this example you see the same strategy. You use inheritance to use the hierarchical concept of both Dogs and Cats that are Animals. But in Python there is no need for this hierarchy. It works equally well
class Dog: def speak(self): print "woff!" class Cat: def speak(self): print "meow" def makeSpeak(a): a.speak() d=Dog() c=Cat() makeSpeak(d) makeSpeak(c)
In Python, you can send a “talk” signal to any object you want. If the object is able to handle it, it will be executed, otherwise it will throw an exception. Suppose you add an Airplane class to both codes and send an Airplane object to makeSpeak. In the case of C ++, it will not compile, since Airplane is not a derived class of Animal. In the case of Python, it will throw an exception at runtime, which might even be the expected behavior.
On the other hand, suppose you add the MouthOfTruth class using the speak () method. In the case of C +, either you have to reorganize your hierarchy, or you have to define a different makeSpeak method to receive MouthOfTruth objects, or in java you can extract the behavior in CanSpeakIface and implement an interface for each. There are many solutions ...
As I would like to point out, I did not find the only reason to use inheritance in Python (except for frameworks and exception trees, but I assume that there are alternative strategies). you do not need to implement a basic hierarchy to do polymorphic work. If you want to use inheritance to reuse the implementation, you can do the same by holding back and delegating, with the added benefit that you can change at runtime, and you clearly define the interface of the contained without risking unintended side effects.
So, in the end, the question is: what is the point of inheritance in Python?
Edit : Thanks for the very interesting answers. Indeed, you can use it to reuse the code, but I am always careful when reusing the implementation. In general, as a rule, I make very shallow inheritance trees or not a tree at all, and if the functionality is shared, I will reorganize it as a general module procedure and then call it from each object. I see the advantage of having one single point of change (for example, instead of adding to Dog, Cat, Moose, etc., I just add to Animal, which is the main advantage of inheritance), but you can achieve the same with the delegation chain (for example, a la JavaScript). I am not saying this is better, though, is another way.
I also found a similar post in this regard.