Inheritance

Inheritance in Python is simpler and more flexible than inheritance in compiled languages such as Java and C++ because the dynamic nature of Python does not impose as many restrictions on the language.

To see how inheritance is used in Python, let’s start with the Square and Circle classes we discussed earlier and generalise them.

If we now want to use these classes in a drawing program, we need to define where on the drawing surface an instance should be located. We can do this by defining x and y coordinates for each instance:

 1>>> class Square:
 2...     def __init__(self, length=1, x=0, y=0):
 3...         self.length = length
 4...         self.x = x
 5...         self.y = y
 6...
 7>>> class Circle:
 8...     def __init__(self, diameter=1, x=0, y=0):
 9...         self.diameter = diameter
10...         self.x = x
11...         self.y = y

This approach works, but leads to a lot of repetitive code when you increase the number of shape classes, as you probably want every shape to have this positional information. This is a standard situation for using inheritance in object-oriented languages. Instead of defining the x and y variables in each shape class, you can abstract them into a general shape class and have each class that defines a particular shape inherit from that general class. In Python, this technique looks like this:

 1>>> class Form:
 2...     def __init__(self, x=0, y=0):
 3...         self.x = x
 4...         self.y = y
 5...
 6>>> class Square(Form):
 7...     def __init__(self, length=1, x=0, y=0):
 8...         super().__init__(x, y)
 9...         self.length = length
10...
11>>> class Circle(Form):
12...     def __init__(self, diameter=1, x=0, y=0):
13...         super().__init__(x, y)
14...         self.diameter = diameter
Lines 6 and 11

Square and Circle inherit from the Form class.

Lines 8 and 13

call the __init__ method of the Form class.

There are generally two requirements when using an inherited class in Python, both of which you can see in the code of the Circle and Square classes:

  1. The first requirement is to define the inheritance hierarchy, which you do by specifying the classes that are inherited from in parentheses immediately after the name of the class, which is defined with the class keyword: Circle and Square both inherit from Form.

  2. The second element is the explicit call to the __init__ method of the inherited class. This is not done automatically in Python, but mostly through the super function, more precisely through the lines super().__init__(x,y). This code calls the initialisation function of Form with the instance to be initialised and the corresponding arguments. Otherwise, the instance variables x and y would not be set for the instances of Circle and Square.

Inheritance also comes into play when you try to use a method that is not defined in the base classes but in the superclass. To see this effect, define another method in the Form class called move that moves a shape in the x and y coordinates. The definition for Form is now:

1>>> class Form:
2...     def __init__(self, x=0, y=0):
3...         self.x = x
4...         self.y = y
5...     def move(self, delta_x, delta_y):
6...         self.x = self.x + delta_x
7...         self.y = self.y + delta_y

If you take the parameters delta_x and delta_y of the method move in the __init__ methods of Circle and Square, you can for example execute the following interactive session:

>>> c = Circle(3)
>>> c.move(4, 5)
>>> c.x
4
>>> c.y
5

The class Circle in the example does not have a move method defined directly in itself, but since it inherits from a class that implements move, all instances of Circle can use the move method. In OOP terms, one could say that all Python methods are virtual – that is if a method does not exist in the current class, the list of superclasses is searched for the method and the first one found is used.