In this article, I am going to cover a very important topic in Programming and Computer Science in general: I am going to teach you the concepts of Object-Oriented Programming (OOP) in Python.
Object-oriented programming is a programming paradigm and it is common in different languages like C++, Java, and of course Python.
The best way to explain what OOP is (and isn’t) is to solve a basic programming exercise without using any OOP concepts, and then see how we can solve the same problem by incorporating object-oriented programming.
If you are more of a video learner, then I have an in-depth video about the fundamental concepts of object-oriented programming. If you prefer the written format, then read on.
A Simple Programming Exercise (No OOP)
Let’s start with this simple example.
Suppose we want to build a program to work with each employee data in some company. Let’s assume we have an external file called employee.txt that stores information about employees including the full name, age, years of experience, and title.
What we want is for our program to read this data from the external file, and store the employee’s first name, last name, age, and salary in memory.
First and last names can be easily deduced from the employee’s full name already stored in the external file.
Let’s also assume that the salary of a specific employee is a function of the employee’s title and years of experience (both of which are also stored in the external file).
The first question we need to tackle is: how can we store and organize this data in memory?
The easiest (but not the best) way would be to use Python lists.
For example, we can define a variable called first_name, which is going to be a list of first names for all the available employees in the original file.
first_name = ["Alice", "Bob", "Sean"]
We can also define another list last_name for the last names, age for the list of ages, and salary for the list of salaries.
In this case, our program would read the employee’s data from the external file, extract the first and last names from the employee’s full name and append them to the first_name, and last_name lists respectively.
For the age, we are going to read it directly from the external file and append it to the age list.
In addition to that, our program is also going to read the employee’s title and years of experience from the external file, compute the salary and append the employee’s salary to the salary list.
With this naive solution, an employee can be identified by an index.
So for an employee with index 5, we can get this employee’s information by reading first_name, last_name, age, and salary.
However, this is not a very elegant solution.
A better way to organize this data is to use a list of lists instead. The idea is to have only one list containing all the relevant information about all employees.
Our final data structure would look something like this:
employee_list = [[firstname, lastname, age, salary], [.., .., .., ..], ...]
In this case, each inner list inside the outer one corresponds to the data of one employee.
Now let’s write some code that would add an employee to the employee_list.
I will intentionally be using pseudo-code here and for the rest of this article.
My goal in this article is not to teach you how to write Python, but to understand the fundamental OOP concepts.
Here is how the pseudo-code of this function will look like. Give yourself 30 seconds to understand what this function is doing. Don’t move on until you fully understand the logic.
def add_employee(employee_data): # employee_data is read from external file # compute first name and last name from full name # compute salary # append this employee to the employee_list
Since the external file employee.txt only has the full name of the employee, we would need to implement some functions, taking the full name as an argument, and returning first and last names.
def extract_firstname(fullname): # some code here return firstname def extract_lastname(fullname): # some code here return lastname
We also want to write a function that would compute the salary of an employee based on their years of experience and title.
def compute_salary(yoe, title): # compute salary from yoe and title return salary
Now that we have all these functions in place, we are ready to implement the add_employee function.
What we need to do here is to just call these functions that we just defined and add an item to the employee_list.
def add_employee(employee_data): fn = extract_firstname(...) ln = extract_lastname(...) salary = compute_salary(...) # read age from employee_data employee_list.append([fn, ln, age, salary])
Let’s revise what we have done so far.
We created a program which takes some data as input from a text file, reads this data and organizes it in a concrete way.
To do that, we defined a function inside which we had to call a group of other functions to extract the first and last names, compute the salary, and finally append those values to the employee_list where we store and organize all employee data.
Basically what we did here is that we solved the big problem by dividing or decomposing the problem into smaller pieces (smaller sub-problems).
We solved these sub-problems by defining separate functions to solve them (extract_firstname, extract_lastname, compute_salary), and finally, we put everything together by calling these sub-problem functions from the big-problem one.
This method of programming is called Procedural Programming. This is how almost everyone starts to learn how to write programs.
Why is our code not ideal?
There are some issues with the way we wrote our program.
The first issue is readability.
Readability is the ability of yourself or someone else who will later read your code to understand it.
Just to give you an easy example, good variable and function names are examples of good practices when it comes to code readability.
So what’s wrong with our code? Why is our code not readable?
Let’s say you want to access the age of a specific employee and suppose you know the index of this employee. Say it is the employee of index 10 in the employee_list.
In order to get the age of this particular employee, we need to access the third element of that inner list at index 10.
print(employee_list) # 10 is the employee index. # 2 is the index where we store the age. # this code, although functional, is terrible.
Why the third element? Because we constructed that list in that specific order such that the age is stored at index 2.
The problem is that the reader of this specific piece of code will have no idea of what you are trying to do here unless they go all the way back to see how you constructed your employee list.
There is nothing in your print statement that says you are trying to print the age of a specific employee.
That’s the reason why this code is terrible from a readability perspective.
The second issue with this code is that data and code are separate.
The functions we defined to extract first and last names and compute the salaries take their input data as arguments.
We have to explicitly pass this data as arguments to each function because these functions don’t understand the context of the problem we are trying to solve.
It would be nice to have some kind of structure which would have the data and code operating on this data as one entity.
We will see what this entity is, and how we can have data and code stored together as one unit in this entity shortly.
Object Oriented Programming (OOP)
There is a different way to solve the same problem, which is by using an object-oriented mindset.
First, we have to forget about solving the big problem by dividing it into separate functions that solve smaller sub-problems.
Instead, we are going to think of the whole problem as a collection of objects, or entities.
Want to Learn OOP in Python?
Check Out My Course on Udemy!
- Write clean code that is reusable, modular, and readable
- Level up your Python programming skills
- Learn the fundamental object-oriented programming concepts
- Work on a project to enhance your understanding of the course
What is an Object?
An object is anything in your code that can be modeled by defined by two things:
- Attributes (also known Properties or Fields) that characterize the object.
- Functions (also known as Methods) that operate and often modifies the object’s attributes.
More specifically, if we look at our problem, we can model the problem as a collection of Employee objects.
In this case, the attributes of an Employee object would be full name, age, years of experience, title.
And the methods could be compute_salary(), extract_firstname(), extract_lastname().
Note that these methods don’t take any arguments!
This is because they are bundled with the object and they can operate on the object’s attributes (data) without having to explicitly pass the data to the function as an argument.
This is what bundling code and data means. It makes the code cleaner, easier to understand, and more straightforward to debug.
Those methods can still take extra arguments. For example, we might want to increase the Employee’s salary by a specific amount for some reason.
In this case, we would need to add a new method.
add_salary(bonus): object.salary += bonus
But the point here is that these methods don’t need to take the object’s attributes as arguments object.salary, because the methods themselves are bundled with the object and have full access to the object’s attributes.
With this approach, the solution to the problem boils down to constructing multiple Employee objects. Each of which will have its own attributes such as name, age, salary, etc and its own methods.
Is Object-Oriented Programming better than Procedural Programming?
Both methods work fine and there is no answer to whether one solution is better than the other. They are two different approaches to solve problems. It all depends on the specific problem you are trying to solve. For some problems, procedural programming may be a better way to approach the solution than using Object-Oriented Programming. For other problems, Object-Oriented Programming could result in a cleaner, more intuitive, and more readable code.
Inheritance is a core concept in object-oriented programming.
To explain the concept of inheritance, we are going to think of another example.
Let’s suppose we are going to work with the OOP approach, but in this case, we need to work with two different kinds of objects: a Human object and an Employee object.
What are the attributes and methods that we need for these objects?
For the Human object, let’s say we have two attributes: name and age attributes and one method: speak().
For the Employee object, let’s say we have five attributes: name, age, employer, salary and title and two methods: speak() and promote().
Right off the bat, you can notice that the Employee object has the same attributes as the Human object (name and age) plus some additional ones.
You can also notice the same thing with the speak method. Both the Employee and the Human have a speak method.
This is not surprising because an Employee IS also a Human!
So it makes sense for an Employee to have all the attributes and methods that a Human has.
This concept is called inheritance.
In other words, we say that the Employee object inherits the Human object.
We can also say that the Employee object is a child of the Human object, or that the Human object is a parent of the Employee object.
The word polymorphism means “many shapes”.
So what does many shapes mean in the context of OOP?
To understand what this means let’s look at the example above of the Human and the Employee objects. Specifically, let’s focus on the speak() method.
This method is the same in both the Human and Employee objects, but it could have different implementations.
It would make sense that the logic behind speak to be slightly different for an Employee that it is for a Human.
For example, the Human object’s speak() method can allow speaking in slang, whereas for the Employee object, a more formal language might be more appropriate.
So in this particular case, although we have the same method (with the same name), it behaves differently depending on the object it is applied to.
In other words, the same method can have many shapes. This is what Polymorphism is.
To really understand encapsulation, we need to talk about two personas first.
The Class Designer: This is the person who designs and implements the class.
The User: This is the person who is going to instantiate the Class, create Objects, call Object’s methods, etc…
These two Personas could be the same person.
For example, say you are writing the code for a car racing game. You decide to structure your code in a way such that you have a Car class, a Player class, a Race class, and so on. Afterwards, you write the code for the game by utilizing all these classes that you designed. In this particular case, you were the Class Designer and the User for the Car, Player, and Race classes.
However, in the same example above, you will probably use some GUI/Graphics libraries in your game that you probably haven’t designed yourself. In this case, you are the User of the Graphics library. Someone else was the designer.
This separation between two personas is important to understand what Encapsulation is.
What is Encapsulation?
Encapsulation means that the User doesn’t (and shouldn’t) know about the internal implementation of the classes that they are using. The only interaction between the User and a class is through the well-defined methods that this class provides.
In other words, a Class is a black box to the user; they just use it to instantiate objects and interact with these objects through the object’s methods and attributes without ever caring how these methods were implemented.
Let’s take the promote() method that we illustrated in the Employee class as an example.
The user can call the promote() method on the Employee object to get an employee promoted, but they don’t need to know what the logic or the implementation behind the method itself is.
Check out the Courses section!
- The Python Learning Path (From Beginner to Mastery)
- Learn Computer Science (From Zero to Hero)
- Coding Interview Preparation Guide
- The Programmer’s Guide to Stock Market Investing
- How to Start Your Programming Blog?
Are you Beginning your Programming Career?
I provide my best content for beginners in the newsletter.
- Python tips for beginners, intermediate, and advanced levels.
- CS Career tips and advice.
- Special discounts on my premium courses when they launch.
And so much more…