Introduction

In inheritance an object is based on another object. When inheritance is implemented, the methods and attributes that were defined in the base class will also be present in the inherited class. This is generally done to abstract away similar code in multiple classes. The abstracted code will reside in the base class and the previous classes will now inherit from the base class.

 

Example

Derived class inherits features from the base class, adding new features to it. This results into re-usability of code. Let’s look at an example of inheritance. In the following example, Rocket is the base class and MarsRover is the inherited class.

class Rocket:
  def __init__(self, name, distance):
    self.name = name
    self.distance = distance

  def launch(self):
    return "%s has reached %s" % (self.name, self.distance)

# Inheriting from the base class
class MarsRover(Rocket): 
  def __init__(self, name, distance, maker):
    Rocket.__init__(self, name, distance)
    self.maker = maker

  def get_maker(self):
    return "%s launched by %s" % (self.name, self.maker)

if __name__ == "__main__":
  x = Rocket("Rocket", "Till stratosphere")
  y = MarsRover("Mars Rover", "Till Mars", "ISRO")
  print(x.launch())
  print(y.launch())
  print(y.get_maker())
  

# Output
Rocket has reached Till stratosphere
Mars Rover has reached Till Mars
Mars Rover Launched by ISRO

 

In the above example __init__() method was defined in both classes. When this happens, the method in the derived class overrides that in the base class. This is to say, __init__() in MarsRover gets preference over the same in Rocket. A better option would be to use the built-in function super(). Two built-in functions isinstance() and issubclass() are used to check inheritances. Function isinstance() returns True if the object is an instance of the class or other classes derived from it. Each and every class in Python inherits from the base class object.

 

Composition

In composition, we do not inherit from the base class but establish relationships between classes through the use of instance variables that are references to other objects. Continuing from the above example, below example illustrate composition

class MarsRoverComp():
  def __init__(self, name, distance, maker):
    self.rocket = Rocket(name, distance) # instantiating the base

    self.maker = maker

  def get_maker(self):
    return "%s launched by %s" % (self.rocket.name, self.maker)


if __name__ == "__main__":
  z = MarsRover("Mars Rover2", "till Mars", "ISRO")
  print(z.launch())
  print(z.get_maker())

 

Multiple Inheritance

When a class derived from more than one base classes, it is called multiple inheritance. In multiple inheritance, the features of all the base classes are inherited into the derived class. The syntax for multiple inheritance is similar to single inheritance.

class Base1:
  pass

class Base2:
  pass

# Derived from classes Base1 and Base2
class MultiDerived(Base1, Base2):
  pass

 

Multilevel Inheritance

A class can inherit form a derived class. This is called multilevel inheritance. In multilevel inheritance, features of the base class and the derived class is inherited into the new derived class.

class Base:
  pass
class Derived1(Base):
  pass
class Derived2(Derived1):
  pass

 

Method Resolution Order

Every class in Python is derived from the class object. It is the most base type in Python. So technically, all other class, either built-in or user-defines, are derived classes and all objects are instances of object class. In the multiple inheritance scenario, any specified attribute is searched first in the current class. If not found, the search continues into parent classes in depth-first, left-right fashion without searching same class twice.

So, in the below example, MultiDerived class the search order is [MultiDerived, Base1, Base2, object].  It ensures that a class always appears before its parents and in case of multiple parents, the order is same as tuple of base classes.

Search order is also called linearization of derived class and the set of rules used to find this order is called Method Resolution Order (MRO). MRO of a class can be viewed as the __mro__ attribute or mro() method.

class Base1:
  pass
class Base2:
  pass
class MultiDerived(Base1, Base2):
  pass
 
# Output: True
print(issubclass(list,object))

# Output: True
print(isinstance("Hello",object))

print(MultiDerived.__mro__)
# Output
(<class '__main__.MultiDerived'>,
 <class '__main__.Base1'>,
 <class '__main__.Base2'>,
 <class 'object'>)