Inheritance
Inheritance is a concept in object-oriented programming where a child class (or subclass) derives attributes and behaviors from a parent or sibling class.
This eliminates the need to implement the methods inherited by a subclass, or child class, again. In other words, it enables a child class to inherit/reuse the attributes and methods of a parent class. In terms of real-world objects, it represents an IS-A relationship.
# Base/super/parent classclass ProgramLanguage:def __init__(self, name):self.name = name# Derived/sub/child classclass Python(ProgramLanguage):pass# Create an object of the derived classy = Python("Inheritance")print(y.name)# Output: Inheritance
In the above code, subclass Python
inherits the variable name
and method __init__
from the base class ProgramLanguage
. The pass
statement acts as a placeholder for the class body.
Reusing Parent Methods
Base class methods can be reused in the derived classes. This prevents redundant code.
In the example below, the derived class, Python
, implements the .say_hi()
parent method inside the .intro()
definition:
# Base classclass ProgramLanguage:def say_hi(self):print("Hi! I am a Programming Language.")# Derived classclass Python(ProgramLanguage):def intro(self):ProgramLanguage.say_hi(self) # Parent class methodprint("Python here!")# Object of the derived classdoc = Python()doc.intro()
The output would be:
Hi! I am a Programming Language.Python here!
Note: Inside the child class, the same number and type of arguments need to be passed into the parent class’ method.
Overriding Methods
In the child class, a parent class method gets overridden when a method of the same name is declared within it.
class Python(ProgramLanguage):def say_hi(self): # Overriding parent methodprint("Hi! I am Python. I am fun!")y = Python()y.say_hi()# Output: Hi! I am Python. I am fun!
If a method is overridden in the child class, the original method can still be accessed by calling the method directly on the parent class name and passing the child class object as an argument:
y = Python()y.say_hi()print("... and now the 'old' ProgramLanguage way of saying hi")# Using the overridden method from parent classProgramLanguage.say_hi(y)
The output would be:
Hi! I am Python.... and now the 'old' ProgramLanguage way of saying hiHi! I am a Programming Language.
Private Variables
When instance variables of the parent class don’t need to be inherited by the child class, they can be made unavailable to the child class by adding double underscores (__
) before the variable name. This appends _classname
before the variable name. So, when we try to access it like other instance variables of the class it gives an “Attribute error”.
class ProgramLanguage:def __init__(self, name):self.name = nameself.__private = 'Private variable'class Python(ProgramLanguage):passy = Python("Python")print(y.name)print(y.__private)
The error would be:
AttributeError: 'Python' object has no attribute '__private'
__private
in parent class ProgramLanguage
is now _ProgramLanguage__private
and can’t be accessed using .__private
. Same is true for class methods.
Note: Employing a double underscore prefix only makes the method or variable inaccessible using the originally declared name. These can still be accessed, like y._ProgramLanguage__private
in the above example. The use of this syntax is an indication of how the variable or method should be treated.
isinstance()
and issubclass()
Python has two built-in functions that work with inheritance:
isinstance()
issubclass()
isinstance()
checks an instance’s type:
isinstance(obj, int)
The code above will be True
only if obj
is an object of class int
or an object of some derived class of int
.
issubclass()
checks class inheritance:
print(issubclass(bool, int)) # Trueprint(issubclass(float, int)) # False
Since bool
is a subclass of int
, it returns True
. However, since float
is not a subclass of int
, it returns False
.
Abstract Base Class
Abstract base classes are inherited rather than instantiated.
from abc import ABC, abstractmethodclass AbstractClass(ABC):def __init__(self, id, name):self.id = idself.name = name@abstractmethoddef calculate_payroll(self):pass
AbstractClass
is derived from ABC
, making it an Abstract Base Class. This tells users of the module that AbstractClass
can not be instantiated, only inherited.
.calculate_payroll()
is decorated with @abstractmethod
, denoting it as an abstract method. This tells developers that if they wish to inherit from AbstractClass
, they must override the .calculate_payroll()
method. Otherwise, the derived class cannot be instantiated.
Types of Inheritance
Single Inheritance
When a child class inherits from only one parent class, it is called single inheritance. All prior code snippets use single inheritance.
Multiple Inheritance
When a child class inherits from multiple parent classes, it is called multiple inheritance.
Python
IS-A ProgramLanguage
and a ScriptLanguage
; it inherits from both these classes:
# Parent 1class ProgramLanguage:def __init__(self, name):self.name = name# Parent 2class ScriptLanguage:def __init__(self, text):self.text = text# Child classclass Python(ProgramLanguage, ScriptLanguage):# Reusing methods/constructors and overriding __init__()def __init__(self, name, text):ProgramLanguage.__init__(self, name)ScriptLanguage.__init__(self, text)# Create object of derived classy = Python("Inheritance","I allow code reusability.")print("Hi! I am {}. {}".format(y.name, y.text))
The output would be:
Hi! I am Inheritance. I allow code reusability.
Multilevel Inheritance
When a derived class inherits from another derived class.
In the code below, class GrandChild()
inherits from class Child
:
# Base 1class Base(object):def __init__(self, name):self.name = namedef getName(self):return self.name# Inherited/sub classclass Child(Base):def __init__(self, name, age):Base.__init__(self, name)self.age = agedef getAge(self):return self.age# Inherited class from sub class aboveclass GrandChild(Child):def __init__(self, name, age, address):Child.__init__(self, name, age)self.address = addressdef getAddress(self):return self.addressg = GrandChild("James", 23, "Transylvania")print(g.getName(), g.getAge(), g.getAddress())# Output: James 23 Transylvania
Hierarchical Inheritance
More than one derived class is created from a single base class:
class ProgramLanguage:def __init__(self, name):self.name = nameclass Python(ProgramLanguage):passclass Java(ProgramLanguage):passy = Python("Python")print(y.name)x = Java("Java")print(x.name)# Output:# Python# Java
Hybrid Inheritance
This form is a blend of more than one type of inheritance mentioned above.
Potential Complications
Class Explosion Problem
Although a program may work as expected, having many classes to support the idea implementation causes the class hierarchy’s inevitable growth. All this leads to the class explosion problem where class hierarchies become too extensive and make code hard to understand and maintain.
Diamond Problem
The diamond problem is an obscurity that arises in multiple inheritance.
Classes, B
and C
, inherit from class A
, and class D
inherits from both B
and C
. If there is a method in A
that B
and C
have overridden and D
does not override it, which version of the method does D
inherit: that of B, or that of C?
class A:def m(self):print("inside A")class B(A):def m(self):print("in B")class C(A):def m(self):print("in C")class D(B, C):passobj = D()obj.m()# Output: in B
The order of parent classes decide which class D
inherits from. To call the parent function of choice, the super()
method is called inside the child class.
All contributors
- jrood
- Christine_Yang
- Anonymous contributor
- brbeck1902
- Anonymous contributor
- Anonymous contributor
- Anonymous contributor
- THE-Spellchecker
Contribute to Docs
- Learn more about how to get involved.
- Edit this page on GitHub to fix an error or make an improvement.
- Submit feedback to let us know how we can improve Docs.