DEV Community

Ahmedur Rahman Shovon
Ahmedur Rahman Shovon

Posted on • Updated on

Inheritance in Python

Inheritance is one of the core topic of object oriented programming. It comes handy in a lot of scenarios while designing or developing a complex software. Python supports inheritance with an use of a minimum number of syntax and semantics.

Inheritance is the mechanism of inheriting the properties and methods of an existing class. The inherited class can add more methods and or properties of its own. The class which is used as basis for inheritance is called the base class or parent class or super class. On the other hand, the class that inherits from the parent class is called the child class or subclass or derived class.

Let's see an example of inheritance:

class Animal:
    def eat(self):
        print("It eats.")
    def sleep(self):
        print("It sleeps.")

class Bird(Animal):
    def fly(self):
        print("It flies in the sky.")

    def sing(self):
        print("It sings.")

print(issubclass(Bird, Animal))

duck = Bird()
print(isinstance(duck, Bird))

duck.eat()
duck.sleep()
duck.fly()
duck.sing()
Enter fullscreen mode Exit fullscreen mode

Here Bird class inherits the Animal class. So, Animal is the parent class (also known as super class or base class) and Bird is the child class (also known as sub class or derived class). The issubclass method ensures that Bird is a subclass of Animal class.

Then we created an instance of Bird class called duck. In this case, the isinstance method ensures that duck is an instance of Bird class. We called the methods of Animal class to check if they are inherited in Bird class. Each animal eats and sleeps. Birds are also animals. So, they inherited the properties of animals. On the contrary, only birds can fly and sing. These two characteristics distinguish them from other animals.

Now, let's help the birds to sing their songs. Install a Python package called pyttsx3 to give life to the bird.

You can simply install the package using pip install pyttsx3.

After installing the package, we are going to create a new file called speaker.py which includes a single class named Speaker. This class will convert any text to sound. The speaker.py contains:

import pyttsx3

class Speaker:
    engine = pyttsx3.init()
    def speak(self, text = "No sound is found."):
        print(text)
        self.engine.say(text)
        self.engine.runAndWait()

Enter fullscreen mode Exit fullscreen mode

Then, add another class called SingingBird that inherits both Bird class and Speaker class in our case study example. It is called Multiple Inheritance. Update the previous code as follow:

from speaker import Speaker
class Animal:
    def eat(self):
        print("It eats.")
    def sleep(self):
        print("It sleeps.")

class Bird(Animal):
    def fly(self):
        print("It flies in the sky.")
    def sing(self):
        print("It sings.")
    def speak(self, text = "No sound is found."):
        print("It can not speak but can write it.")
        print(text)

class SingingBird(Speaker, Bird):
    def __init__(self, birdName = "Bird Name"):
        self.birdName = birdName
    def printBirdName(self):
        print("This is a {birdName}!".format(birdName = self.birdName))

print(SingingBird.__bases__)

duck = SingingBird("Duck")
duck.printBirdName()
duck.eat()
duck.sleep()
duck.fly()
duck.sing()
duck.speak("Quack quack, quack quack, quack quack")

Enter fullscreen mode Exit fullscreen mode

The output looks like:

(<class '__main__.Bird'>, <class 'speaker.Speaker'>)
This is a Duck!
It eats.
It sleeps.
It flies in the sky.
It sings.
Quack quack, quack quack, quack quack
Enter fullscreen mode Exit fullscreen mode

The __bases__ attribute of SingingBird shows (<class '__main__.Bird'>, <class '__main__.Speaker'>) which ensures the multiple inheritance of that class. So, we see that Python supports Multiple Inheritance like a charm.

We see that speak method exists in both Bird and Speaker class. As SingingBird inherits both Bird class and Speaker class, why speak method of Speaker class is being invoked? The answer is SingingBird invokes the first speak method of it's base classes. If a method is implemented differently by two or more of the super classes, the order of the class method should be maintained.

The methods in the earlier classes override the methods in later ones. So, if we create SingingBird like class SingingBird(Bird, Speaker) it would override (making the bird mute!) the speak method of the Speaker class. Reversing their order like this: class SingingBird(Speaker, Bird) would make the speak method of Speaker class accessible.

All codes can be found in this gist.

Thanks to David Griffin for giving vital suggestions to improve this post.

Inheritance

Latest comments (5)

Collapse
 
arsho profile image
Ahmedur Rahman Shovon • Edited

In our case, we did not need to create an object of Speaker class. But this did not make it an abstract class. In Python to declare a class as abstract class, we need to use abc module. Here is the official documentation on Abstract Base Classes in Python

Collapse
 
habilain profile image
David Griffin

I know this is a trivial example, but for the love of anything, don't import inside functions! Inside a function you'll be executing the import statement every time the function is called, which is generally agreed to be A Bad Thing ®.

Incidentally, while I'm unfamiliar with the library, it seems pretty likely that the code would be better performing with a global initialisation of pyttsx3 and engine as a global variable (if you had multiple ducks, obviously), but that's comparatively minor.

Collapse
 
arsho profile image
Ahmedur Rahman Shovon

Unlike Java, Python modules aren't imported multiple times. Just running import two times will not reload the module. In this scenario, we could create two files; one for Speaker class and another for other classes. In Speaker class we could load the module at the top. But for simplicity of explaining the inheritance which is the core subject of the tutorial, I have merged them in a single file. But this operation will not effect the performance or reload the pyttsx3 as I mentioned earlier. Thank you for pointing out a nice feature of Python.

Collapse
 
habilain profile image
David Griffin • Edited

While I was referring to best practices in programming (and remember, Python's style guide explicitly states where imports should go), with the view that best practice is absolutely something we should follow while teaching newcomers, the claim of performance being identical is demonstrably false.

import timeit
import string # warmup this to avoid any potential bias

def func_a():
  import string
  for x in range(10000):
      x += 1  # dummy operation so the loop does something we can compare against

def func_b():
  import string
  f = lambda x: x + 1
  for x in range(10000):
      f(x)

def func_c():
  for x in range(10000):
    import string

# Measurements on my laptop, an ageing i7-4500u, but trends should be reproducible
timeit.timeit('func_a()', globals=globals(), number=100) # Gives ~= 0.068
timeit.timeit('func_b()', globals=globals(), number=100) # Gives ~= 0.126
timeit.timeit('func_c()', globals=globals(), number=100) # Gives ~= 0.368
Enter fullscreen mode Exit fullscreen mode

So, point of fact, import is actually a pretty expensive operation; it's 6 times more expensive than simple bytecode ops, and three times more expensive than a function call.

Thread Thread
 
arsho profile image
Ahmedur Rahman Shovon

Updated the post to follow the best practices. Thank you a lot for pointing out those. Really appreciate your explanation and effort.