Python Topics : Polymorphism, Overloading & Overriding
What is Polymorphism?
the word polymorphism means having many forms
in programming polymorphism means the same function name (but different signatures) being used for different types
the key difference is the data types and number of arguments used in function

Example of built-in polymorphic function
# Python program to demonstrate in-built polymorphic functions

# len() being used for a string
print(len("geeks"))

# len() being used for a list
print(len([10, 20, 30]))
Example of user-defined polymorphic function
# A simple Python function to demonstrate polymorphism

def add(x, y, z = 0): 
    return x + y+z

# Driver code 
print(add(2, 3))
print(add(2, 3, 4))
Polymorphism with Class Methods
class India():
    def capital(self):
        print("New Delhi is the capital of India.")

    def language(self):
        print("Hindi is the most widely spoken language of India.")

    def type(self):
        print("India is a developing country.")

class USA():
    def capital(self):
        print("Washington, D.C. is the capital of USA.")

    def language(self):
        print("English is the primary language of USA.")

    def type(self):
        print("USA is a developed country.")

obj_ind = India()
obj_usa = USA()
for country in (obj_ind, obj_usa):
    country.capital()
    country.language()
    country.type()
Polymorphism with Inheritance
polymorphism allows defining methods in the child class that have the same name as the methods in the parent class
with inheritance the child class inherits the methods from the parent class
it is possible to modify a method in a child class that it has inherited from the parent class
useful in cases where the method inherited from the parent class doesn't quite fit the child class
in such cases re-implement the method in the child class
process of re-implementing a method in the child class is known as Method Overriding
class Bird:
    def intro(self):
        print("There are many types of birds.")
    
    def flight(self):
        print("Most of the birds can fly but some cannot.")
  
class sparrow(Bird):
    def flight(self):
        print("Sparrows can fly.")
    
class ostrich(Bird):
    def flight(self):
        print("Ostriches cannot fly.")
    
obj_bird = Bird()
obj_spr = sparrow()
obj_ost = ostrich()

obj_bird.intro()
obj_bird.flight()

obj_spr.intro()
obj_spr.flight()

obj_ost.intro()
obj_ost.flight()
Polymorphism with a Function and Objects
is possible to create a function that can take any object, allowing for polymorphism
any instantiated object will be able to be used with this function
can call their action using the same func() function
def func(obj):
    obj.capital()
    obj.language()
    obj.type()
 
obj_ind = India()
obj_usa = USA()
 
func(obj_ind)
func(obj_usa)
Method Overloading
two or more methods have the same name but different numbers of parameters or different types of parameters, or both
these methods are called overloaded methods and this is called method overloading
Python does not support method overloading by default
there are different ways to achieve method overloading in Python
the problem with method overloading in Python is can only use the latest defined method
# first product method.
# takes two argument and print their product
def product(a, b):
    p = a * b
    print(p)

# second product method takes three arguments and print their product
def product(a, b, c):
    p = a * b*c
    print(p)

# uncommenting the below line shows an error
# product(4, 5)

# this line will call the second product method
product(4, 5, 5)
Overloading : Method 1
can use the arguments to make the same function work differently i.e. as per the arguments
not very efficient
# Function to take multiple arguments
def add(datatype, *args):

    # if datatype is int
    # initialize answer as 0
    if datatype == 'int':
        answer = 0

    # if datatype is str
    # initialize answer as ''
    if datatype == 'str':
        answer = ''

    # Traverse through the arguments
    for x in args:
        # This will do addition if the arguments are int. Or concatenation if the arguments are str
        answer = answer + x
    print(answer)

# Integer
add('int', 5, 6)

# String
add('str', 'Hi ', 'Geeks')
Overloading : Method 2
can achieve method overloading in python by user defined function using None keyword as default parameter
below when arguments are passed to add method
  • the method checks if both the parameters are available or not
  • given default parameter values as 'None', if any of the value is not passed it will remain 'None'
  • using If-Else statements, can achieve method overloading by checking each parameter as single statement
# code
def add(a=None, b=None):
    # Checks if both parameters are available
    # if statement will be executed if only one parameter is available
    if a != None and b == None:
        print(a)
    # else will be executed if both are available and returns addition of two
    else:
        print(a+b)


# two arguments are passed, returns addition of two
add(2, 3)
# only one argument is passed, returns a
add(2)
not the most efficient way to overload
makes code more complex with multiple if/else statement
is not the desired way to achieve the method overloading
Overloading : Method 3
use Multiple Dispatch Decorator
to install Multiple Dispatch Decorator
pip3 install multipledispatch
example
from multipledispatch import dispatch

# passing one parameter
@dispatch(int, int)
def product(first, second):
    result = first*second
    print(result)

# passing two parameters
@dispatch(int, int, int)
def product(first, second, third):
    result = first * second * third
    print(result)

# can also pass data type of any value as per requirement
@dispatch(float, float, float)
def product(first, second, third):
    result = first * second * third
    print(result)


# calling product method with 2 arguments
product(2, 3)  # this will give output of 6

# calling product method with 3 arguments but all int
product(2, 3, 2)  # this will give output of 12

# calling product method with 3 arguments but all float
product(2.2, 3.4, 2.3)  # this will give output of 17.985999999999997
Dispatcher creates an object which stores different implementation
at runtime it selects the appropriate method depending on the type and number of parameters passed
Method Overriding
method overriding allows a subclass or child class to provide a specific implementation of a method which is already provided by one of its super-classes or parent classes
when a method in a subclass has the same name, the same parameters or signature, and same return type(or sub-type) as a method in its super-class, the method in the subclass is said to override the method in the super-class
the version of a method that is executed will be determined by the object that is used to invoke it
if an object of a parent class is used to invoke the method, then the version in the parent class will be executed
if an object of the subclass is used to invoke the method, then the version in the child class will be executed
it is the type of the object being referred to (not the type of the reference variable) which determines which version of an overridden method will be executed
# Python program to demonstrate 
# Defining parent class 
class Parent(): 
    
    # Constructor 
    def __init__(self): 
        self.value = "Inside Parent"
        
    # Parent's show method 
    def show(self): 
        print(self.value) 
        
# Defining child class 
class Child(Parent): 
    
    # Constructor 
    def __init__(self): 
        super().__init__()  # Call parent constructor
        self.value = "Inside Child"
        
    # Child's show method 
    def show(self): 
        print(self.value) 
        
# Driver's code 
obj1 = Parent() 
obj2 = Child() 

obj1.show()  # Should print "Inside Parent"
obj2.show()  # Should print "Inside Child"
Method Overriding with Multiple & Multilevel Inheritance
an example where want to override a method of one parent class only
# Python program to demonstrate 
# Defining parent class 1 
class Parent1(): 
        
    # Parent's show method 
    def show(self): 
        print("Inside Parent1") 
        
# Defining Parent class 2 
class Parent2(): 
        
    # Parent's show method 
    def display(self): 
        print("Inside Parent2") 
        
        
# Defining child class 
class Child(Parent1, Parent2): 
        
    # Child's show method 
    def show(self): 
        print("Inside Child") 
    
        
# Driver's code 
obj = Child() 

obj.show() 
obj.display()
Multilevel Inheritance
child and grandchild relationship
# Python program to demonstrate 
# Python program to demonstrate overriding in multilevel inheritance 
class Parent(): 
        
    # Parent's show method 
    def display(self): 
        print("Inside Parent") 
    
# Inherited or Sub class (Note Parent in bracket) 
class Child(Parent): 
        
    # Child's show method 
    def show(self): 
        print("Inside Child") 
    
# Inherited or Sub class (Note Child in bracket) 
class GrandChild(Child): 
        
    # Child's show method 
    def show(self): 
        print("Inside GrandChild")         
    
# Driver code 
g = GrandChild() 
g.show() 
g.display()
Calling the Parent's Method within the Overridden Method
parent class methods can also be called within the overridden methods
can generally be achieved by two ways
  • using Classname - Parent's class methods can be called by using the Parent classname.method inside the overridden method
    # Python program to demonstrate 
    # calling the parent's class method 
    # inside the overridden method 
    class Parent(): 
        
        def show(self): 
            print("Inside Parent") 
            
    class Child(Parent): 
        
        def show(self): 
            
            # Calling the parent's class 
            # method 
            Parent.show(self) 
            print("Inside Child") 
            
    # Driver's code 
    obj = Child() 
    obj.show()
  • using Super() - super() function provides the facility to refer to the parent class explicitly
    useful when need to call superclass functions
    it returns the proxy object that allows us to refer parent class by 'super'

    using super() example 1
    # Python program to demonstrate calling the parent's class method 
    # inside the overridden method using super() 
    class Parent(): 
        
        def show(self): 
            print("Inside Parent") 
            
    class Child(Parent): 
        
        def show(self): 
            
            # Calling the parent's class 
            # method 
            super().show() 
            print("Inside Child") 
            
    # Driver's code 
    obj = Child() 
    obj.show()
    using super() example 2
    # Program to define the use of super() function in multiple inheritance 
    class GFG1: 
        def __init__(self): 
            print('HEY !!!!!! GfG I am initialised(Class GEG1)') 
        
        def sub_GFG(self, b): 
            print('Printing from class GFG1:', b) 
        
    # class GFG2 inherits the GFG1 
    class GFG2(GFG1): 
        def __init__(self): 
            print('HEY !!!!!! GfG I am initialised(Class GEG2)') 
            super().__init__() 
        
        def sub_GFG(self, b): 
            print('Printing from class GFG2:', b) 
            super().sub_GFG(b + 1) 
        
    # class GFG3 inherits the GFG1 ang GFG2 both 
    class GFG3(GFG2): 
        def __init__(self): 
            print('HEY !!!!!! GfG I am initialised(Class GEG3)') 
            super().__init__() 
        
        def sub_GFG(self, b): 
            print('Printing from class GFG3:', b) 
            super().sub_GFG(b + 1) 
        
        
    # main function 
    if __name__ == '__main__': 
        
        # created the object gfg 
        gfg = GFG3() 
        
        # calling the function sub_GFG3() from class GHG3 
        # which inherits both GFG1 and GFG2 classes 
        gfg.sub_GFG(10)
index