Guide to Classes and Object-Oriented Programming in Python
Object-oriented programming (OOP) is a popular programming paradigm for writing software. Before the development of object-oriented programming, procedural programming was an efficient way of building or writing software.
In procedural programming, programs are divided into interdependent functions, which is a straightforward process. However, due to the interdependence of functions, a change in one function can break several other functions.
Object-oriented programming was developed to solve this problem.
As a software developer, object-oriented programming will give you a deeper understanding of your code by learning what happens line by line and the ideas underlying it.
In this tutorial, we will guide you through:
- Understanding the concepts of object-oriented programming in Python
- How to create a class and instantiate objects in Python.
- The object-oriented programming pillars in Python
Let’s dive in!
What is Object-Oriented Programming (OOP) in Python?
Object-oriented programming is a programming model that provides a method of structuring data using objects and classes. These objects represent real-world situations and events. The goal of object-oriented programming is to combine properties and methods that work together as an individual unit.
For example, think of a car as an object with properties like brand name, model, color, size, etc. A car also has methods such as movement, speed, etc. These properties and methods are structured into an individual object or unit. This process of structuring is known as object-oriented programming.
Python is an object-oriented programming language that enables developers to create applications primarily focusing on code reusability. In Python, everything is an object consisting of methods and properties, making it easy for you to build classes and objects.
The main idea of object-oriented programming in Python is not just for data representation but to determine how the program is organized.
Classes and objects in Python
Simply put, a class is a blueprint or prototype for creating objects. To create an object, we need a class.
A class is a template that defines a collection of attributes and methods that specify the behavior that an object can carry out with its data. For instance, let us use a book as a class. A book has properties that contain the details of a book, such as a title, author, year of publication, etc. The book itself is the object.
Once we have a defined set of properties, our class becomes an object. It allows the creation of as many objects as required using the same class. Let’s define a class to get a better understanding.
Defining a class in Python
A class is defined using the class
keyword, followed by the class name, which is usually a word describing the class.
ℹ️ Class names in Python are in camel case. Each word starts with a capital letter.
In this section, we will define a book class that stores data about a book.
class Book:
…
Code language: Python (python)
In the code, we created a book class using the class
keyword. Within this class is where we will define our attributes. Subsequently, we will add a body using custom methods, but before that, the placeholder lets us run this code without throwing an error.
Instantiate an object in Python
While a class is a sketch or blueprint of an object, an object is a set of attributes and functions built on real-world data. The process of creating an object in Python is called instantiation. An object is instantiated by adding the name of the class, Book, followed by an opening and closing bracket.
Here is an example:
>>> Book()
Code language: Python profile (profile)
These book examples are basic and not reflective yet of real-life applications. Before we get into more practical applications, we will first need to understand what dunder methods are and what they do.
Creating classes and objects with dunder methods
Dunder stands for “double underscores.” Methods that begin and end with double underscores are known as dunder methods.
Dunder methods provide the functionality of a class when a specific event occurs in a program. Multiple dunder methods are used when working with classes and objects in Python. This article will focus on the init()
method.
Init method
The __init__()
method is also known as a constructor. It is a unique Python method that is automatically executed whenever a class is used to instantiate an object. We use the __init__()
method when adding data or attributes to a new class
object.
When defining the
method, we are to add some parameters such as __init__()
self
and other parameters like name
, title
, age
, etc., depending on the attributes.
Let us create a class and a set of objects using the
method. Several common attributes of a book can be added, such as title, author, and year of publication:__init__()
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.year = year
# instantiating a new object
book1 = Book("Americanah", " Chimamanda Adichie", 2013)
book2 = Book("Teller of secrets", "Bisi Adjapon", 2021)
book3 = Book("Stay with me", "Ayobami Adebayo", 2017)
print(book1.title)
Code language: Python (python)
Output
>>> Americanah
Code language: Python profile (profile)
In the code above, we created a Book
class with an
function that initializes three parameters: __init__()
title
, author
, and year
.
Next, we passed three parameters, although there are four arguments in the
method. This is because the __init__()
self
parameter is only required when defining a method. It is not necessary to include it when calling the method.
Also, conventionally, the self
parameter must come before other parameters. Python subsequently uses this parameter to create the Book
instance.
Lastly, we instantiated a new object by adding values to the object. You can access any of the instance attributes of the Book
instance using the dot notation.
ℹ️ Attributes are variables that can be accessed using instances. Example:
book1.title
.
The self parameter
The self
parameter is a representation of the class instance. This parameter allows you to access the attributes in a class. In Python, it is a convention that theself
parameter is to be provided first before other parameters in the instance method; otherwise, it will result in an error.
class Book:
def __init__():
print("This is a bookstore")
object = Book()
print("Welcome")
Code language: Python (python)
Output:
Traceback (most recent call last):
File "/Users/anitaachu/character-encoding/app.py", line 5, in <module>
object = Book()
TypeError: __init__() takes 0 positional arguments but 1 was given
Code language: Python profile (profile)
ℹ️ The
self
parameter is not a Python keyword. Therefore, you can use any other word instead of “self.” However, it is good programming practice to useself
.
Modifying and deleting objects
Imagine you had to review the books in your store, and you realized one of the books had the wrong year of publication. You can easily modify the attributes of objects in the Book
class.
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.year = year
# instantiating a new object
book1 = Book("Americanah", " Chimamanda Adichie", 2016)
book2 = Book("Teller of secrets", "Bisi Adjapon", 2021)
book3 = Book("Stay with me", "Ayobami Adebayo", 2017)
# modifying attribute
book1.year = 2013
print(book1.year)
Code language: Python (python)
When the code is executed, you will have this result:
>>> 2013
Code language: Python profile (profile)
You can also delete object values or the object itself using the del
method.
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.year = year
# instantiating a new object
book1 = Book("Americanah", " Chimamanda Adichie", 2016)
book2 = Book("Teller of secrets", "Bisi Adjapon", 2021)
book3 = Book("Stay with me", "Ayobami Adebayo", 2017)
# deleting attribute
del book2.year
print(book2.year)
When the code is executed, you will have this result:
Traceback (most recent call last):
File "/Users/anitaachu/character-encoding/app.py", line 15, in <module>
print(book2.year)
AttributeError: 'Book' object has no attribute 'year'
Code language: Python (python)
Object-oriented programming principles
The primary object-oriented programming principles include:
- Encapsulation
- Inheritance
- Polymorphism
These principles are referred to as the pillars of object-oriented programming. Each concept is a broad topic and would take several articles to explore thoroughly. However, this article will discuss the basics of these concepts to give you a good understanding of what they are.
Encapsulation
Encapsulation is the process of restricting users’ access to specific class attributes or methods by only allowing the attributes to be available via particular methods.
ℹ️ Encapsulation is often referred to as data hiding.
With encapsulation, you can only share or expose what is needed for the user. The purpose of encapsulation is to prevent unauthorized modification of data.
In encapsulation, object attributes are made private and can only be modified by the object method. These objects’ attributes are known as private attributes.
How encapsulation works
The data contained in an object and the methods that manipulate the data are wrapped together in one unit. This restricts the variables that can be accessed.
For example, imagine you’re working with an organization that has salespeople and admins. The admins have access to all records of sales and finances. In a situation where a salesperson needs access to sales records, they cannot access this data without contacting an admin and requesting access to the sales record. This is encapsulation.
In Python, we use getters and setters to implement encapsulation. The getters and setters are methods that give us access to and modify the values of the private attributes, respectively.
In Python, we use double underscore __
to declare a private attribute. Let’s add a private attribute to the Book
class.
class Book:
def __init__(self, title, author, year, price):
# public attributes
self.title = title
self.author = author
self.year = year
# private attribute
self.__price = price
def __repr__(self):
return f"Book: {self.title}, Author: {self.author}, Year: {self.year}, Price: {self.price}"
# instantiating a new object
book1 = Book("Americanah", " Chimamanda Adichie", 2016, 6000)
book2 = Book("Teller of secrets", "Bisi Adjapon", 2021, 5000)
book3 = Book("Stay with me", "Ayobami Adebayo", 2017, 4000)
print(book3.title)
print(book3.author)
print(book3.year)
print(book3.__price)
Code language: Python (python)
Output
Stay with me
Ayobami Adebayo
2017
Traceback (most recent call last):
File "/Users/anitaachu/character-encoding/app.py", line 26, in <module>
print(book3.__price)
AttributeError: 'Book' object has no attribute '__price'
Code language: Python profile (profile)
In the code above, price
is a private attribute. Since it cannot be accessed outside the class, the output prints all the attributes except the price
attribute.
However, we can access the private attribute using getter and setter methods:
class Book:
# constructor
def __init__(self, title, author, year, price):
# public data
self.title = title
self.author = author
self.year = year
# private attribute
self.__price = price
# getter method
def get_price(self):
return self.__price
# setter method
def set_price(self, price):
self.__price = price
def __repr__(self):
return f"Book: {self.title}, Author: {self.author}, Year: {self.year}, Price: {self.get_price()}"
# instantiating a new object
book1 = Book("Americanah", " Chimamanda Adichie", 2016, 6000)
book2 = Book("Teller of secrets", "Bisi Adjapon", 2021, 5000)
book3 = Book("Stay with me", "Ayobami Adebayo", 2017, 4000)
# modifying price using setter
book2.set_price(4000)
print(book2)
Code language: Python (python)
Output
Book: Teller of secrets, Author: Bisi Adjapon, Year: 2021, Price: 4000
Code language: Python profile (profile)
Bravo! We got the private attribute and the modified price of book2
.
However, bear in mind that these private attributes are not really protected. For example, we can access the price
attribute using book1._Book__price
.
class Book:
# constructor
def __init__(self, title, author, year, price):
# public data
self.title = title
self.author = author
def __repr__(self):
return f"Book: {self.title}, Author: {self.author}, Year: {self.year}, Price: {self.get_price()}"
# instantiating a new object
book1 = Book("Americanah", " Chimamanda Adichie", 2016, 6000)
book2 = Book("Teller of secrets", "Bisi Adjapon", 2021, 5000)
book3 = Book("Stay with me", "Ayobami Adebayo", 2017, 4000)
# modifying price using setter
book1._Book__price
self.year = year
# private attribute
self.__price = price
# getter method
def get_price(self):
return self.__price
# setter method
def set_price(self, price):
self.__price = price
Code language: Python (python)
Output
Book: Teller of secrets, Author: Bisi Adjapon, Year: 2021, Price: 4000
Code language: Python profile (profile)
Inheritance
Inheritance is considered the most fundamental principle of object-oriented programming. Inheritance refers to the ability of one class to derive its methods and attributes from another class.
In this case, the class that derives the attribute is known as the child class, while the class from which the child class derives its attributes is known as the parent class. The primary purpose of inheritance is to aid the reusability of code.
The child class inherits all the attributes and functions of the parent class. Therefore, we do not have to write the same lines of code repeatedly. We can also add more features without changing the class.
How inheritance works
To demonstrate, we’ll use our book example. A book is a class with common attributes like title, author, price, and year of publication. This holds true for both fiction and nonfiction books — which means that both types of books will inherit those particular attributes.
Using the bookstore example, we can add a Fiction
and a Nonfiction
class, which will inherit the title
, author
, year
, and price
attributes.
class Book:
def __init__(self, title, author, year, price):
# public data
self.title = title
self.author = author
self.year = year
# private attribute
self.__price = price
# getter method
def get_price(self):
return self.__price
# setter method
def set_price(self, price):
self.__price = price
def __repr__(self):
return f"Book: {self.title}, Author: {self.author}, Year: {self.year}, Price: {self.get_price()}"
# child class
class Fiction(Book):
def __init__(self, title, author, year, price):
super().__init__(title, author, year, price)
# child class
class Non_fiction(Book):
def __init__(self, title, author, year, price):
super().__init__(title, author, year, price)
# instantiating a new object
fiction1 = Fiction("Stay with me", "Ayobami Adebayo", 2017, 4000)
non_fiction1 = Non_fiction("There Was a Country", "Chinua Achebe", 2012, 6000)
print(fiction1)
Code language: Python (python)
When you run your terminal, you should get the following output:
Book: Stay with me, Author: Ayobami Adebayo, Year: 2017, Price: 4000
Code language: Python profile (profile)
Polymorphism
Polymorphism is another fundamental concept in Python programming. In literal terms, polymorphism refers to the occurrence of a similar thing in various forms. Polymorphism in Python refers to the ability of an object, class, or method to take different forms at different instances. A subclass, also known as a child class, can adopt a method of its parent class through polymorphism.
How polymorphism works
In polymorphism, classes can implement inherited methods in different ways. This enables us to define methods in a subclass with the same name as in its parent class. In some situations, the subclass may modify its parent class’s method where the parent class’s method does not fit. In this case, the method must be reimplemented in the child class.
For a better understanding, let us work with the bookstore example again:
class Book:
def __init__(self, title, author, year, price):
# public data
self.title = title
self.author = author
self.year = year
# private attribute
self.__price = price
# getter method
def get_price(self):
return self.__price
# setter method
def set_price(self, price):
self.__price = price
def __repr__(self):
return f"Book: {self.title}, Author: {self.author}, Year: {self.year}, Price: {self.get_price()}"
# child class
class Fiction(Book):
def __init__(self, title, author, year, price):
super().__init__(title, author, year, price)
def __repr__(self):
return f"Book: {self.title}, Author: {self.author}, Year: {self.year}, Price: {self.get_price()}"
# instantiating a new object
fiction1 = Fiction("Stay with me", "Ayobami Adebayo", 2017, 4000)
print(fiction1)
Code language: Python (python)
Output
Book: Stay with me, Author: Ayobami Adebayo, Year: 2017, Price: 4000
Code language: Python profile (profile)
In the code above, the parent class, Book
, contains the repr
method, which returns the string representation of an object. The Fiction
subclass also uses this method; it is invoked whenever an object is printed.
In addition, when the child class inherits a method from the parent class, polymorphism allows us to modify the method in the child class to fit its needs. This process of modification is known as the overriding method.
Here is an example:
class MobileDevice:
def __init__(self, name, color, price):
self.name = name
self.color = color
self.price = price
def details(self):
print(f"Details: {self.name}, Color: {self.color}, Price: {self.price}")
def battery(self):
print("Mobile device has one battery")
def camera(self):
print("Mobile phones have dual camera")
class Iphone(Mobile_device):
def details(self):
print(f"Details: {self.name}, Color: {self.color}, Price: {self.price}")
def battery(self):
print("Mobile device has one battery")
def camera(self):
print("Mobile device has three cameras")
mobile_device = Mobile_device('Pixel 6','Gold', 50000 )
mobile_device.details()
mobile_device.battery()
mobile_device.camera()
ios = Iphone('iPhone 13 Pro','Green', 65000)
ios.details()
ios.battery()
ios.camera()
Code language: Python (python)
Output
Details: Pixel 6, Color: Gold, Price: 50000
Mobile device has one battery
Mobile phones have dual camera
Details: iPhone 13 Pro, Color: Green, Price: 65000
Mobile device has one battery
Mobile device has three cameras
Code language: Python (python)
As a result of polymorphism, the child class (Iphone
) overrides the camera()
method inherited from its parent class.
Wrapping up
In this tutorial, we introduced the concept of objects and classes in Python and the fundamental principles of object-oriented programming.
These concepts do not only apply to Python. Object-oriented programming applies to most programming languages, such as Java, C#, C++, JavaScript, etc. However, their implementation differs according to the language.
I hope this tutorial was of help to you.
Happy Coding! 🙂
Anita Achu is a software developer and technical writer passionate about creating technical content to aid developers to learn about software development tools and how to use these tools in building and providing solutions to problems.