Python Inheritance

Prerequisite

About inheritance

  • Aquiring methods and attributes/properties from other class to the current class.
  • say class B inherit class A then
    • A is parent/base class to class B and
    • B is child/derived class to class A

When to use inheritance?

  • when you have common properties and/or methods
  • It's mostly for code reusability

Quck example

  • Let's say in an organization we have HR, Engineer, Legal Advisor
  • All all employees of the organization so, they share common properties like name, id, address, dob, salary, etc.
  • But, they will have different designation
  • They also share common behaviours like profile/details, etc.
  • Let's write the code.
class Employee:
    def __init__(self, uid, name, role, salary):
        self.uid = uid
        self.name = name
        self.role = role
        self.salary = salary

    def get_details(self):
        data = {
            "id": self.uid,
            "name": self.name,
            "role": self.role
        }
        return data

class HR(Employee):
    def __init__(self, uid, name):
        role = "HR"
        salary = 50000
        super().__init__(uid, name, role, salary)

class Engineer(Employee):
    def __init__(self, uid, name):
        role = "Engineer"
        salary = 40000
        super().__init__(uid, name, role, salary)

class Advisor(Employee):
    def __init__(self, uid, name):
        role = "Advisor"
        salary = 60000
        super().__init__(uid, name, role, salary)

# let's create objects
hr = HR("AB1", "John")
engineer = Engineer("AB2", "Akshaya")
advisor = Advisor("AB3", "Shiva")

for emp in [hr, engineer, advisor]:
    print(emp.get_details())
  • Above code gives the below output
{'id': 'AB1', 'name': 'John', 'role': 'HR'}
{'id': 'AB2', 'name': 'Akshaya', 'role': 'Engineer'}
{'id': 'AB3', 'name': 'Shiva', 'role': 'Advisor'}
  • If you look at the above code we have written most of the code in parent/base class Employee and inherited it in all child classes like HR, Engineer, Advisor.
  • we reused parent class methods __init__, get_details in the child classes
  • "super" allows us to call the parent/base class methods.

Take aways

  • Code reusability

  • Less code

  • Readability

  • Maintainability [i.e less code modifications for common feature]

Types of inheritance

  1. Single inheritance
  2. Multi-level inheritance
  3. Multiple inheritance
  4. Hierarchical Inheritance
  5. Hybrid inheritance

Single inheritance

  • A derived class is created from a single base class.
class A:
    pass

class B(A):
    pass

Multi-level inheritance

  • a derived class is created from another derived class.
class A:
    pass

class B(A):
    pass

class C(B):
    pass
  • In above code, A inherits B and C inherits B
  • B will have properties of A
  • C will have properties of both A and B

Multiple inheritance

  • A derived class is created from more than one base class.
class A:
    pass

class B:
    pass

class C(A, B):
    pass
  • In above code, A and B are base classes
  • C aquires properties from both A and B
  • No dependency between A and B

Hierarchical Inheritance

  • More than one derived class is created from a single base class and further child classes act as parent classes for more than one child class.
class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B):
    pass

class E(B):
    pass

class F(C):
    pass

class G(C):
    pass
  • From the above code, class A has two children B and C. Further, B and C both are having two children D and E; and F and G respectively.
  • It forms a tree like structure.

Hybrid inheritance

  • It's a combination of more than one inheritance.
  • Hence, it may be a combination of Multilevel and Multiple or Hierarchical and Multilevel or Hierarchical, Multilevel and Multiple inheritances.
  • Try avoid this kind of inheritance.
  • Keep your code simple and loosely coupled.

Method Resolution Order - MRO

This case occurs when we inherit multiple classes with same attibutes and/or methods then the order of method/attributes execution priority changes as below

child class > first inherited class > next inherited class > ...

Let's look at an example

class A:
    def who_are_you(self):
        print("I'm A")

class B:
    def who_are_you(self):
        print("I'm B")

class C:
    def who_are_you(self):
        print("I'm C")

class D(B, C, A):
    pass

class E(B, C, A):
    def who_are_you(self):
        print("I'm E")

class F(A, B, C):
    pass

d = D()
d.who_are_you()
# ouput: I'm B
e = E()
e.who_are_you()
# ouput: I'm E
f = F()
f.who_are_you()
# ouput: I'm A
  • from the above code we can see that child class has high priority over inherited classes.
  • first inherited class will have the next priority and so on.