A key concept of object-oriented programming is that of inheritance. In this tutorial, we’re going to see how basic inheritance works in Python. Inheritance defines a way for a given class to inherit attributes and methods (variables and functions) from one or more base classes. This technique gives programmers the ability to centralize common functionality and data in a single place instead of having it spread out and duplicated across multiple classes. Let’s look at a few examples of Inheritance in Python now.
A Python Class
class Superclass():
def __init__(self, color, height, width):
self.height = height
self.color = color
self.width = width
def does_stuff(self):
print('This method does stuff')
Above is a simple Python class. It has three attributes and one method.
Inheriting From A Superclass
class Subclass(Superclass):
pass
obj1 = Subclass('Red', '5 feet', 100)
In this code here, we define a new class of Subclass and make it inherit from Superclass. We use the pass keyword in this Subclass to show that it has no data or behavior of its own. Then we create an object out of this Subclass and assign it to obj1.
The Child Has Now Inherited Attributes And Methods
print(type(obj1))
print(isinstance(obj1, Subclass))
print(obj1.color)
print(obj1.height)
print(obj1.width)
obj1.does_stuff()
<class '__main__.Subclass'> True Red 5 feet 100 This method does stuff
What this code here does is use type() and isinstance() to show that obj1 is an object of type Subclass. Further, since it inherits from Superclass, it has access to all of the attributes and methods of the parent class.
Classes Without Inheritance
class Inputdevice:
def __init__(self, devicetype, inputconnector, bluetooth, manufacturer):
self.devicetype = devicetype
self.manufacturer = manufacturer
self.inputconnector = inputconnector
self.bluetooth = bluetooth
class Outputdevice:
def __init__(self, devicetype, connector, manufacturer, outrate):
self.devicetype = devicetype
self.manufacturer = manufacturer
self.outrate = outrate
self.connector = connector
class IODevice:
def __init__(self, devicetype, connector, manufacturer, outrate):
self.devicetype = devicetype
self.manufacturer = manufacturer
self.outrate = outrate
self.connector = connector
input1 = Inputdevice("Keyboard", "usb", True, "Lenovo")
io1 = IODevice("Flash Drive", "usb", "Sandisk", "35MB ps")
output1 = Outputdevice("Monitor", "HDMI", "Samsung", "18Gbps")
print("This device has a " + input1.inputconnector + " connector")
print(io1.manufacturer + " is the device manufacturer")
print(input1.manufacturer + " " + input1.devicetype)
print(output1.manufacturer + " " + output1.devicetype)
This device has a usb connector Sandisk is the device manufacturer Lenovo Keyboard Samsung Monitor
In this code, you can see there are three classes. There’s an Inputdevice, Outputdevice, and IOdevice class. Each one of these classes represents a type of peripheral, and each of them has a set of attributes that are relevant to that peripheral type. So Inputdevices have a devicetype and a manufacturer along with an inputconnector and the ability to use bluetooth. An IOdevice also has a devicetype and a manufacturer, but they have a generic connector instead of an inputconnector. Outputdevices have a devicetype and manufacturer too, while also having an outrate and a connector. The output from running the code gives us what we would expect to see. At this state, each of these is a standalone implementation of it’s own class but there’s a considerable amount of duplication among the data that each class holds. We can do better using inheritance.
Rewriting Classes With Inheritance
class Peripheral:
def __init__(self, devicetype, manufacturer):
self.devicetype = devicetype
self.manufacturer = manufacturer
class Outputperipheral(Peripheral):
def __init__(self, devicetype, manufacturer, connector, outrate):
super().__init__(devicetype, manufacturer)
self.outrate = outrate
self.connector = connector
class Inputdevice(Peripheral):
def __init__(self, devicetype, inputconnector, bluetooth, manufacturer):
super().__init__(devicetype, manufacturer)
self.inputconnector = inputconnector
self.bluetooth = bluetooth
class Outputdevice(Outputperipheral):
def __init__(self, devicetype, connector, manufacturer, outrate):
super().__init__(devicetype, manufacturer, connector, outrate)
class IODevice(Outputperipheral):
def __init__(self, devicetype, connector, manufacturer, outrate):
super().__init__(devicetype, manufacturer, connector, outrate)
input1 = Inputdevice("Keyboard", "usb", True, "Lenovo")
io1 = IODevice("Flash Drive", "usb", "Sandisk", "35MB ps")
output1 = Outputdevice("Monitor", "HDMI", "Samsung", "18Gbps")
print("This device has a " + input1.inputconnector + " connector")
print(io1.manufacturer + " is the device manufacturer")
print(input1.manufacturer + " " + input1.devicetype)
print(output1.manufacturer + " " + output1.devicetype)
This device has a usb connector Sandisk is the device manufacturer Lenovo Keyboard Samsung Monitor
The original code is now rewritten to make use of inheritance. We have a better organization of these classes and made it easier to introduce new classes by implementing some inheritance and class hierarchy. Running this new code, you can see that the output is the same as before. So we’re getting the same results, but with better code organization, which is one of the main benefits of inheritance. We now can add properties that are specific to each kind of peripheral just in one place and would only have one place to edit them if we needed to change the names of any of these attributes in the future.