OOPs Concepts in Python
Overview
Object-oriented programming (OOP) is a programming pattern based on the concept of objects. Objects consist of data and methods. The object's data are its properties, which define what it is. And the object's methods, are its functions, that define what the object can do. Object-oriented style of programming is very popular because of its ability to map the virtual world entities i.e. our code, to real-world objects. OOPS concepts are widely used by many popular programming languages due to the several advantages it provides.
In this article, we will study the OOPS concepts in python programming.
Introduction
OOPS concepts in Python are very closely related to our real world, where we write programs to solve our problems. Solving any problem by creating objects is the most popular approach in programming.
This approach is termed as Object Oriented Programming. Object-oriented programming maps our code instructions with real-world problems, making it easier to write and simpler to understand. They map real-world entities(such as companies and employees) as 'software objects' that have some 'data' associated with them and can perform some 'functions'.
What are OOPS Concepts in Python?
OOPS in programming stand for Object Oriented Programming System. It is a programming paradigm or methodology, to design a program using classes and objects OOPS treats every entity as an object.
Object-oriented programming in Python is centered around objects. Any code written using OOPS is to solve our problem but is represented in the form of Objects. We can create as many objects as we want, for a given class.
So what are objects? Objects are anything that has properties and some behaviors. The properties of objects are often referred to as variables of the object, and behaviors are referred to as the functions of the objects. Objects can be real-life or logical.
Suppose, a Pen is a real-life object. The property of a pen includes its color, and type (gel pen or ball pen). And, the behavior of the pen may include that, it can write, draw, etc.
Any file in your system is an example of a logical object. Files have properties like file_name, file_location, file_size and their behaviors include they can hold data, can be downloaded, shared, etc.
Some Major Benefits of OOPS Include:
- They reduce the redundancy of the code by writing clear and reusable codes (using inheritance).
- They are easier to visualize because they completely relate to real-world scenarios. For example, the concept of objects, inheritance, and abstractions, relate very closely to real-world scenarios(we will discuss them further in this article).
- Every object in OOPS represent a different part of the code and has its own logic and data to communicate with each other. So, there are no complications in the code.
Class and Objects in Python
Suppose you wish to store the number of books you have, you can simply do that by using a variable. Or, say you want to calculate the sum of 5 numbers and store it in a variable, well, that can be done too!
Primitive data structures like numbers, strings, and lists are designed to store simple values in a variable. Suppose, your name, or square of a number, or count of some marbles (say).
But what if you need to store the details of all the Employees in your company? For example, you may try to store every employee in a list, you may later be confused about which index of the list represents what details of the employee(e.g. which is the name field, or the empID etc.)
Example:
Even if you try to store them in a dictionary, after an extent, the whole codebase will be too complex to handle. So, in these scenarios, we use Classes in python.
A class is used to create user-defined data structures in Python. Classes define functions, which are termed methods, that describe the behaviors and actions that an object created from a class can perform. OOPS concepts in Python majorly deal with classes and objects.
Classes make the code more manageable by avoiding complex codebases. It does so, by creating a blueprint or a design of how anything should be defined. It defines what properties or functions, any object which is derived from the class should have.
IMPORTANT:
A class just defines the structure of how anything should look. It does not point to anything or anyone in particular. For example, say, HUMAN is a class, which has suppose -- name, age, gender, city. It does not point to any specific HUMAN out there, but yes, it explains the properties and functions any HUMAN should or any object of class HUMAN should have.
An instance of a class is called the object. It is the implementation of the class and exists in real.
An object is a collection of data (variables) and methods (functions) that access the data. It is the real implementation of a class.
Consider this example, here Human is a class - It is just a blueprint that defines how Human should be, and not a real implementation. You may say that "Human" class just exists logically.
However, "Ron" is an object of the Human class (please refer to the image given above for understanding). That means, Ron is created by using the blueprint of the Human class, and it contains the real data. "Ron" exists physically, unlike "Human" (which just exists logically). He exists in real, and implements all the properties of the class Human, such as, Ron have a name, he is 15 years old, he is a male, and lives in Delhi. Also, Ron implements all the methods of Human class, suppose, Ron can walk, speak, eat, and sleep.
And many humans can be created using the blueprint of class Human. Such as, we may create 1000s of more humans by referring to the blueprint of the class Human, using objects.
Quick Tip:
class = blueprint(suppose an architectural drawing). The Object is an actual thing that is built based on the ‘blueprint’ (suppose a house). An instance is a virtual copy (but not a real copy) of the object.
When a class is defined, only the blueprint of the object is created, and no memory is allocated to the class. Memory allocation occurs only when the object or instance is created. The object or instance contains real data or information.
How to Define a Class in Python?
Classes in Python can be defined by the keyword class, which is followed by the name of the class and a colon.
Syntax:
Indented code below the class definition is considered part of the class body.
'pass' is commonly used as a placeholder, in the place of code whose implementation we may skip for the time being. "pass" allows us to run the code without throwing an error in Python.
What is an _init_ Method?
The properties that all Human objects must have been defined in a method called init(). Every time a new Human object is created, __init__() sets the initial state of the object by assigning the values we provide inside the object’s properties. That is, __init__() initializes each new instance of the class.
__init__() can take any number of parameters, but the first parameter is always a variable called self.
The self parameter is a reference to the current instance of the class. It means, the self parameter points to the address of the current object of a class, allowing us to access the data of its(the object's) variables.
So, even if we have 1000 instances (objects) of a class, we can always get each of their individual data due to this self because it will point to the address of that particular object and return the respective value.
Note:
We can use any name in place of self, but it has to be the first parameter of any function in the class.
Let us see, how to define __init__() in the Human class:
Code:
In the body of .__init__(), we are using the self variable 3 times, for the following:
self.name = 'name' creates an attribute called name and assigns to it the value of the name parameter.
self.age = age attribute is created and assigned to the value of age parameter passed.
self.gender = gender attribute is created and assigned to the value of gender parameter passed.
There are 2 types of attributes in Python:
1. Class Attribute:
These are the variables that are the same for all instances of the class. They do not have new values for each new instance created. They are defined just below the class definition.
Code:
Here, the species will have a fixed value for any object we create.
2. Instance Attribute:
Instance attributes are the variables that are defined inside of any function in class. Instance attributes have different values for every instance of the class. These values depend upon the value we pass while creating the instance.
Code:
Here, name, age, and gender are the instance attributes. They will have different values for new instances of the class.
For properties that should have a similar value per instance of a class, use class attributes. For properties that differ per instance, use instance attributes.
Creating an Object in Class
When we create a new object from a class, it is called instantiating an object. An object can be instantiated by the class name followed by the parentheses. We can assign the object of a class to any variable.
Syntax:
As soon as an object is instantiated, memory is allocated to them. So, if we compare 2 instances of the same class using '==', it will return false(because both will have different memory assigned).
Suppose, we try to create objects of our Human class, then we also need to pass the values for name, age, and gender.
Code:
Here, we have created 2 objects of the class Human passing all the required arguments.
Warning: If we do not pass the required arguments, it will throw a TypeError: TypeError: init() missing 3 required positional arguments: 'name', 'age', and 'gender'.
Let us now see, how to access those values using objects of the class. We can access the values of the instances by using dot notation.
Code:
Output:
So, we find that we can access the instance and class attributes just by using the dot operator.
Code:
Output:
In the above example, we have our class attributes values same "Homo Sapiens", but the instance attributes values are different as per the value we passed while creating our object.
However, we can change the value of class attributes, by assigning classname.classAttribute with any new value.
Code:
Output:
Instance Methods
An instance method is a function defined within a class that can be called only from instances of that class. Like init(), an instance method's first parameter is always self.
Let's take an example and implement some functions can class Human can perform --
Code:
Output:
This Human class has two instance methods:
- speak(): It returns a string displaying the name of the Human.
- eat(): It has one parameter "favouriteDish" and returns a string displaying the favorite dish of the Human.
Having gained a thorough knowledge of what Python classes, objects, and methods are, it is time for us to turn our focus toward the OOP core principles, upon which it is built.
Fundamentals of OOPS in Python
There are four fundamental concepts of Object-oriented programming –
- Inheritance
- Encapsulation
- Polymorphism
- Data abstraction
Let us now look into each of the OOPS concepts in Python deeply.
Inheritance
People often say to newborn babies that they have got similar facial features to their parents, or that they have inherited certain features from their parents. It is likely that you too have noticed that you have inherited some or the other features from your parents.
Inheritance too is very similar to the real-life scenario. But here, the "child classes" inherit features from their "parent classes." And the features they inherit here are termed as "properties" and "methods"!
Inheritance is the process by which a class can inherit or derive the properties(or data) and methods(or functions) of another class. Simply, the process of inheriting the properties of a parent class into a child class is known as inheritance.
The class whose properties are inherited is the Parent class, and the class that inherits the properties from the Parent class is the Child class.
Let us see the syntax of inheritance in Python:
Code:
So, we define a normal class as we were defining in our previous examples. Then, we can define the child class and mention the parent class name, which it is inheriting in parentheses.
Code:
Output:
In the above example, the child class Boy is inheriting the parent class Human. So, when we create an object of the Boy class, we can access all the methods and properties of its parent class, Human, because it is inheriting it.
Also, we have defined a method in the Boy class, which is schoolName . The method schoolName cannot be accessed by the parent class object. But, we can obviously call schoolName method by creating the child class object(Boy).
Let's see the issue we face if we are trying to call child class's methods using parent class's object:
Code:
Output:
So, here we get the AttributeError: 'Human' object has no attribute 'schoolName'. Because the child classes can access the data and properties of parent class but vice versa is not possible.
Super()
The super() function in python is a inheritance-related function that refers to the parent class. We can use it to find the method with a particular name in an object’s superclass. It is a very useful function. Let us see how it works --
Syntax: This is the syntax of the super function. We write the super() keyword followed by the method name we want to refer from our parent class.
Code:
Output:
Here, we have defined the dance() method in Human and Girl classes. But, both methods have different implementations as you can see. In Human class, the dance method says "I can dance", whereas in Girl class, the dance method says "I can do classical dance". So, let us call the parent class's dance method from the child class.
You can see, in line 19, we are calling the dance method using super().dance(). This will call the dance method from the Human class. So, it prints "I can dance". Although, there was already an implementation for dance() in Girl(at line 15).
When we call any method using super(), the method in the superclass will be called even if there is a method with the same name in the subclass.
Polymorphism
Suppose, you are scrolling through your Instagram feeds on your phone. You suddenly felt like listening to some music as well, so you opened Spotify and started playing your favorite song. Then, after a while, you got a call, so you paused all the background activities you were doing, to answer it. It was your friend's call, asking you to text the phone number of some person. So, you messaged him the number, and resumed your activities.
Did you notice one thing? You could scroll through feeds, listen to music, attend/make phone calls, message -- everything just with a single device - your Mobile Phone! Whoa!
So, Polymorphism is something similar to that. 'Poly' means multiple and 'morph' means forms. So, polymorphism altogether means something that has multiple forms. Or, 'some thing' that can have multiple behaviours depending upon the situation.
Polymorphism in OOPS refers to the functions having the same names but carrying different functionalities. Or, having the same function name, but different function signature(parameters passed to the function).
A child class inherits all properties from its parent class methods. But sometimes, it wants to add its own implementation to the methods. There are ample of ways we can use polymorphism in Python.
Example of Inbuilt Polymorphic Functions
len() is an example of inbuilt polymorphic function. Because, we can use it to calclate the length of vaious types like string, list, tuple or dictionary, it will just compute the result and return.
Code:
Output:
Here we have passed a string, list and dictionary to the len function and it computed the result. So, it is an example of an inbuilt Polymorphic function.
We also have polymorphism with the '+' addition operator. We can use it to 'add' integers or floats or any arithmetic addition operation. In the other hand, with String, it performs the 'concatenation' operation.
Code:
Output:
So, we can see that a single operator '+' has been used to carry out different operations for distinct data types.
Polymorphism with Class Methods
We can perform polymorphism with the class methods. Let's see how:
Code:
Output:
Here, we can iterate over the objects of Monkey & Rabbit using one variable - animal, and it can call the instance methods of both of them. So, here one variable animal is used to represent the behaviour (color() & eats()) of Monkey as well as Rabbit. So, it is following the rules of Polymorphism!
Polymorphism with Inheritance
We can have polymorphism with inheritance as well. It is possible to modify a method in a child class that it has inherited from the parent class, adding its own implementation to the method. This process of re-implementing a method in the child class is known as Method Overriding in Python. Here is an example that shows polymorphism with an inheritance:
Code:
Output:
Here, the Square and Triangle class has overriden the method of the shape class. So, here the method no_of_sides has different implementations with respect to different shapes. So, it is in line with Polymorphism.
Encapsulation
You must have seen medicine capsules, where all the medicines remain enclosed inside the cover of the capsule. Basically, a capsule encapsulates several combinations of medicine.
Similarly, in programming, the variables and the methods remain enclosed inside a capsule called the 'class'! Yes, we have learned a lot about classes in Python and we already know that all the variables and functions we create in OOP remain inside the class.
The process of binding data and corresponding methods (behavior) together into a single unit is called encapsulation in Python.
In other words, encapsulation is a programming technique that binds the class members (variables and methods) together and prevents them from being accessed by other classes. It is one of the concepts of OOPS in Python.
Encapsulation is a way to ensure security. It hides the data from the access of outsiders. An organization can protect its object/information against unwanted access by clients or any unauthorized person by encapsulating it.
Getters and Setters
We mainly use encapsulation for Data Hiding. We do so by defining getter and setter methods for our classes. If anyone wants some data, they can only get it by calling the getter method. And, if they want to set some value to the data, they must use the setter method for that, otherwise, they won't be able to do the same. But internally, how these getter and setter methods are performed remains hidden from the outside world.
Code of getter & setter in Encapsulation
Output:
In the above example, we defined the getter getBookName() and setter setBookName() to get and set the names of books respectively. So, now we can only get and set the book names upon calling the methods, otherwise, we cannot directly get or modify any value. This promotes high security to our data, because others are not aware at a deep level of how the following methods are implemented(if their access is restricted).
We can also promote safety of our data using access modifiers. Let's see what are access modifiers.
Access Modifiers
Access modifiers limit access to the variables and methods of a class. Python provides three types of access modifiers private, public, and protected.
In Python, we don’t have direct access modifiers like public, private, and protected. We can achieve this by using single underscore and double underscores.
- Public Member: Accessible anywhere from outside the class.
- Private Member: Accessible only within the class
- Protected Member: Accessible within the class and it's sub-classes
Single underscore _ represents Protected class. Double underscore __ represents Private class.
Suppose we try to make an Employee class:
Code:
Here, we have made the employee's name public, employee's ID protected, and the employee's salary private. Suppose we try to print all the values. Now, we will be able to access the employee's name or his ID, but not the salary(because it is private). Look into the error below:
Output:
However, we can access the employee's salary by calling that getter method getSalary() we have created in our Employee class.
Now, you might have understood the importance of getters and setters, and also the access modifiers which restricts the access to your data.
Complete code:
Output:
We can access private members from outside of a class by creating public method to access private members (just like we did above). There is one more method to get access called name mangling.
A protected data member is used when inheritance is used and you want the data members to have access only to the child classes.
So, encapsulation protects an object from unauthorized access. It allows private and protected access levels to prevent accidental data modification.
Abstraction
It is likely that you are reading this article on your laptop, phone, or tablet. You are also probably making notes, and highlighting important points, and you may be saving some points in your internal files while reading it. As you read this, all you see before you is a 'screen' and all this data that is shown to you. As you type, all you see are the keys on the keyboard and you don't have to worry about the internal details, like how pressing a key may lead to displaying that word onscreen. Or, how clicking on a button on your screen could open a new tab!
So, everything we can see here is at an abstract level. We are not able to see the internal details, but just the result it is producing(which actually matters to us).
Abstraction in a similar way just shows us the functionalities anything holds, hiding all the implementations or inner details.
The main goal of Abstraction is to hide background details or any unnecessary implementation about the data so that users only see the required information. It helps in handling the complexity of the codes.
Key Points of Abstract Classes
- Abstraction is used for hiding the background details or any unnecessary implementation of the data, so that users only see the required information.
- In Python, abstraction can be achieved by using abstract classes
- A class that consists of one or more abstract methods is called the "abstract class".
- Abstract methods do not contain any implementation of their own.
- Abstract class can be inherited by any subclass. The subclasses that inherit the abstract classes provide the implementations for their abstract methods.
- Abstract classes can act like blueprint to other classes, which are useful when we are designing large functions. And the subclass which inherits them can refer to the abstract methods for implementing the features.
- Python provides the abc module to use the abstraction
Syntax of Abstract Class in Python
To use abstraction, it is mandatory for us to import ABC class from the abc module.
Syntax:
ABC stands for Abstract Base class. The abc module provides the base for defining Abstract Base classes (ABC).
Let us take an example and see the implementation of abstract class:
So, the above code for your reference is:
Code:
Output:
Here, we have an abstract class Vehicle. It is abstract because it is inheriting the abstract class abc. The class Vehicle have an abstract method called no_of_wheels, which do not have any definition, because abstract methods are not defined(or abstract methods remain empty, and they expects the classes inheriting the abstract classes to provide the implementation for the method ).
But, other classes which inherits the Vehicle class, like Bike, Tempo or Truck, defines the method no_of_wheels, and they provide their own implementation for the abstract method. Suppose, bike have 2 wheels, so it prints "Bike have 2 wheels" in the inherited abstract method no_of_wheels. And, similarly, Tempo and Truck classes also provide their own implementations.
Some notable points on Abstract classes are:
- Abstract classes cannot be instantiated. In simple words, we cannot create objects for the abstract classes.
- An Abstract class can contain the both types of methods -- normal and abstract method. In the abstract methods, we do not provide any definition or code. But in the normal methods, we provide the implementation of the code needed for the method.
Advantages of OOPS in Python
There are numerous advantages of OOPS concepts in Python, making it favorable for writing serious softwares. Let us look into a few of them --
- Effective problem solving because, for each mini-problem, we write a class that does what is required. And then we can reuse those classes, which makes it even quicker to solve the next problem.
- Flexibility of having multiple forms of a single class, through polymorphism
- Reduced high complexity of code, through abstraction.
- High security and data privacy through encapsulation.
- Reuse of code, by the child class inheriting properties of parent class through inheritance.
- Modularity of code allows us to do easy debugging, instead of looking into hundreds of lines of code to find a single issue.
Conclusion
In this article, we learned about concepts of the trendiest programming paradigm - OOPS concepts in Python. After this, you must have understood that OOPS is a paradigm that completely deals with the concept of objects! And how closely this concept is influenced by real-life aspects, like inheritance, abstraction, polymorphism, etc. Let us recap the points we went through in this article:
- OOP stands for Object-oriented programming and it deals with objects.
- POP stands for Procedure Oriented Programming, and it involves a set of procedure to operate.
- Class is the blueprint of an object. It is used to declare and create objects. Class is a logical entity. Example: Car is a class.
- Object is an instance of class. Object is a physical entity. And we can create as many objects as we want. Example: Audi, BMW, Maruti, etc are objects of class Car
- Inheritance is an OOP concept, where existing classes can be modified by a new class. The existing class is called the base class and the new class is called the derived class.
- Polymorphism in OOP allows an object to take many forms. Simply, polymorphism allows us to perform the same action in many different ways.
- Encapsulation in OOP is the process of wrapping up variables and methods into a single entity.
- Abstraction in OOP is a process of hiding the real implementation of the method by only showing a method signature.
With that, we come to the end of this tutorial. I hope you enjoyed it thoroughly.