Learning Python: Inheritance and Polymorphism
By: Michael Mcmillan
Tags: Python Programming, Python Object, Learning Python, Object Oriented, Learn To Code

Photo by ThisisEngineering RAEng on Unsplash

One of the key reasons you should adopt object-oriented programming (OOP) in your Python programming is to take advantage of two key OOP concepts — inheritance and polymorphism. In this article I’ll discuss how to use both concepts in your Python programming.

Inheritance Terminology

Inheritance is the ability of a class definition to use attributes and methods that have been defined in another class. The class that first defines a set of attributes and methods is called a super class. The class that inherits these attributes and methods is called a sub class.

The set of a super class and its sub classes is called the inheritance hierarchy. Sub classes can be derived from other sub classes. This is called multiple inheritance but I am not going to explore it in this article.

Inheritance Basics

A sub class is created by providing a reference to the super class in the sub class’s definition. To demonstrate this, I am going to create a Shape class that will serve as a super class to a set of sub classes that define specific types of shapes, such as circles and squares.

Here is a Shape class definition and a short program to test the class:

class Shape:

def __init__(self,name,x,y):

self.name = name



self.x = x

self.y = y def draw(self):

print("Drawing",self.name,"at origin x:",self.x,"y:",self.y) s = Shape("Shape",1,2)

s.draw()

The output from this program is:

Drawing Shape at x: 1 y: 2

Now let’s define a Rectangle class that inherits from the Shape class. We start by recognizing Rectangle is a sub class by placing the name of the sub class in parentheses in the first line of the derived class’s definition:

class Rectangle(Shape):

The next thing to do is define the __init__ method. We want to take advantage of inheritance by not having to redefine methods we can reuse in our sub class definition. With that in mind, here’s a definition for the Rectangle class __init__ method:

def __init__(self,x,y,height,width):

Shape.__init__(self,x,y)

self.name = "Rectangle"

self.height = height

self.width = width

The main thing to note here is how I call the __init__ method from the super class Shape so I don’t have to write out that definition again. This is the essence of good code reuse via inheritance.

Next we need a method that “draws” a rectangle. Here, I decided to demonstrate a concept known as overriding, which means I am going to redefine what the draw method does when it is invoked from a Rectangle object. Here is the definition:

#overriding super class definition of the draw method

def draw(self):

print("Drawing",self.name,"at origin x:",self.x,"y:",self.y)

print("Height:",self.height,"Width:",self.width)

Now let’s combine all this into one program that creates both a Shape object and a Rectangle object, along with the definitions for these classes:

class Shape:

def __init__(self,x,y):

self.name = "Shape"

self.x = x

self.y = y def draw(self):

print("Drawing",self.name,"at origin x:",self.x,"y:",self.y) class Rectangle(Shape):

def __init__(self,x,y,height,width):

Shape.__init__(self,x,y)

self.name = "Rectangle"

self.height = height

self.width = width #overriding base class definition

def draw(self):

print("Drawing",self.name,"at origin x:",self.x,"y:",self.y)

print("Height:",self.height,"Width:",self.width) sh = Shape(3,4)

sh.draw()

rec = Rectangle(1,2,5,10)

rec.draw()

Here is the output from this program:

Drawing Shape at origin x: 3 y: 4

Drawing Rectangle at origin x: 1 y: 2

Height: 5 Width: 10

To summarize where we are with inheritance, we have seen that a sub class can inherit attributes and methods of a super class so that those attributes and methods don’t have to be redefined in the sub class. This is a powerful programming feature that allows code reuse and allows programmers to follow the principle “Don’t Repeat Yourself” or DRY.

This is also an example of class polymorphism in Python. The system will figure out what type of object it has encountered and the proper method is called.

Other Polymorphism Types in Python

Because Python is a dynamically-typed programming language, it is easy to process collections of class objects. This is not true in a language like C++, which is strongly-typed and requires the programmer to jump through some hoops to properly process class object collections.

In Python, I can simply declare a new list and then add objects to the list as I please. When I want to access the objects, I can simply loop through the list and perform whatever operation I want. Python will know what to do with each method call

In the example below, I create a list of Shape -based objects and “draw” them from inside the list. Here’s the code and the output:

class Shape:

def __init__(self,x,y):

self.name = "Shape"

self.x = x

self.y = y def draw(self):

print("Drawing",self.name,"at origin x:",self.x,"y:",self.y) class Rectangle(Shape):

def __init__(self,x,y,height,width):

Shape.__init__(self,x,y)

self.name = "Rectangle"

self.height = height

self.width = width #overriding base class definition

def draw(self):

print("Drawing",self.name,"at origin x:",self.x,"y:",self.y)

print("Height:",self.height,"Width:",self.width) class Circle(Shape):

def __init__(self, x,y,radius):

Shape.__init__(self,x,y)

self.name = "Circle"

self.radius = radius def draw(self):

print("Drawing",self.name,"at origin x:",self.x,"y:",self.y)

print("Radius:",self.radius) sh = Shape(3,4)

rec = Rectangle(1,2,5,10)

circ = Circle(5,6,5)

shapes = []

shapes.append(sh)

shapes.append(rec)

shapes.append(circ)

print()

print()

print("Drawing set of shapes:")

for sh in shapes:

sh.draw()

The output from this program is:

Drawing set of shapes:

Drawing Shape at origin x: 3 y: 4

Drawing Rectangle at origin x: 1 y: 2

Height: 5 Width: 10

Drawing Circle at origin x: 5 y: 6

Radius: 5

Because of polymorphism, Python knows which draw method to call even though all the objects are stored in the same list.

Function Polymorphism

While we’re on the subject on polymorphism, I also need to cover function polymorphism. There are lots of examples of built-in function polymorphism in Python. The print function is one good example.

You can pass a number, a string, a list, and many other objects to the print function and it will know how to handle that object appropriately. The len function is another example. If you pass a string to the len function, it will print the number of characters in the string. If you pass len a list, it will count the elements in the list. If you pass len a dictionary, it will count the key/value pairs. That is another excellent example of function polymorphism.

We can do the same thing with class objects. The following function prints the name of a Shape object passed to it:

def printName(shape):

print(shape.name)

Here is a program that uses the function:

sh = Shape(3,4)

rec = Rectangle(1,2,5,10)

circ = Circle(5,6,5)

printName(sh)

printName(rec)

printName(circ)

Here is the output from this program:

Shape

Rectangle

Circle

This completes what I want to say about class inheritance and polymorphism. You now have a fairly complete, though brief, introduction to the major features of Python. In my next article I will present a grab bag of features I didn’t cover in previous articles as I wrap up this series on Learning Python.

Thanks for reading and please email me at mmmcmillan1@att.net with your comments and suggestions or add a response below.