Python is a versatile programming language that enables you to solve problems through procedural and object-oriented programming (OOP) paradigms.
This tutorial will teach you the following:
What OOP means
Its fundamental concepts
The various methods associated with it, and
How to apply it in solving problems.
To get the best out of this tutorial, you need to have a basic knowledge of Python and be familiar with using a text editor or IDE.
What is object-oriented programming (OOP)?
Object-oriented programming (OOP) is a programming method that uses the concepts of classes and objects to organize and structure data.
What are classes and objects?
A class is a data type that acts as a template definition for a particular kind of object. It is also known as the blueprint of an object. You can create a Python class using the keyword class and giving it a name. An example is as follows:
class Phone:
pass
While an object is an element or instance of a class. You can think of the object as an actor that acts within a system. You can create an object from the class. By adding parenthesis to the class name. For example:
phone = Phone()
N/B: The phone is the variable that holds your object's instance.
Examples of classes and their objects:
Class | Object |
Blueprint of a phone. | iPhone 6s, Samsung A23 |
Animal | Dog, cat |
Food | Pasta, cereal, or meat |
Variables in object-oriented programming
Variables in OOP are attached to objects instead of being defined solely. They are called attributes. You can use these attributes in the following ways:
Describe the object: For example, the phone's color or the number of pages in a book.
Hold the state: This describes the state of an object. For example, the floor of a room if it is wet or dry.
Adding attributes to a class
Knowing what attributes you should add to your class is very important. You need to tell the class what attributes it should have at construction time when you create an object. There is a special function you call at the time of creation called the constructor.
A constructor is a special function. That you only invoke when you first create your object. You can only call the constructor once. In this method, you create the attributes the object should have. Additionally, you assign any starter value to the attribute after creation.
In Python, the constructor has the name __init__()
. You must also pass a Special parameter, (self)
, To the constructor. The parameter (self)
refers to the object's instance.
N/B: If you do not add an attribute to
(self)
, Python will treat it as a temporary variable that will not exist after__init__()
is done executing.
Example of adding and initializing attributes on a class:
class Circle:
def __init__(self, radius):
self.radius = radius
# To create the object
circle = Circle(5)
print(circle.radius)
The code snippet above describes the class Circle with a variable, radius. __init__()
is called implicitly. You do not call it by name, but when you create the object. As seen in this line of code circle = Circle(5)
Methods in object-oriented programming
In OOP, you need methods to process your inputs and produce useful results. In this section, you will learn about these methods and their various applications.
Encapsulation: Protect your data
Encapsulation is a process of protecting your object's data from uncontrolled outside manipulation.
One way to carry out this task in Python is by adding two leading underscores as a prefix to your attribute name. Here is an example of how to encapsulate:
class Circle:
def __init__(self, radius):
self.__radius = radius # Private attribute with two leading underscores as the prefix
As with many rules and recommendations, there are usually exceptions. Sometimes, You want to change your data. In this case, you can employ the getters and setters method.
N/B: This is one of many ways to encapsulate your data. Python does not enforce access restrictions as rigorously as other programming languages.
Getters and setters or accessors and mutators, are methods dedicated to reading or changing your data. Getters make your inner data readable to the outside, while setters can change your data directly and also act as a Guard. To avoid a wrong value from being set.
Using decorators for getters and setters helps you write a more concise and readable code. An example of a decorator is the @property
decorator. How to use the @property
decorator:
class Circle:
def __init__(self, radius):
self.__radius = radius # Private attribute with two leading underscores as the prefix
@property
def radius(self):
return self.__radius
@radius.setter
def radius(self, value):
if value < 0:
raise Exception("Radius cannot be negative")
self.__radius = value
# Create an instance of Circle
circle = Circle(5)
# Accessing attributes using the @property decorator
print("Radius:", circle.radius)
# Modifying the radius using the setter
circle.radius = 4
# Accessing the updated attributes
print("Updated Radius:", circle.radius)
Output:
Radius: 5
Updated Radius: 4
In the code snippet above, the @property
decorator acts as a getter for the 'radius' attribute, while the @radius setter
decorator is the setter. The setter allows you to modify the radius to ensure it is not negative. Any negative number passed into it will raise the exception "Radius cannot be negative."
Inheritance
Inheritance is the ability of a class to inherit attributes from another class. The newly formed class is called the child class or subclass, while the initial class is called the parent class or superclass. You inherit from a class by creating a new class and putting the name of the parent class in parentheses. For example:
class Circle:
def __init__(self, radius):
self.__radius = radius # Private attribute with two leading underscores as the prefix
@property
def radius(self):
return self.__radius
@radius.setter
def radius(self, value):
if value < 0:
raise Exception("Radius cannot be negative")
self.__radius = value
class ColoredCircle(Circle):
def __init__(self, radius, color):
super().__init__(radius)
self.color = color
# Create an instance of Circle
circle = Circle(5)
# Accessing attributes using the @property decorator
print("Circle Radius:", circle.radius)
# Modifying the radius using the setter
circle.radius = 7
# Accessing the updated attributes
print("Updated Circle Radius:", circle.radius)
# Create an instance of ColoredCircle
colored_circle = ColoredCircle(6, "blue")
# Accessing attributes of ColoredCircle
print("Colored Circle Radius:", colored_circle.radius)
print("Colored Circle Color:", colored_circle.color)
Output:
Circle Radius: 5
Updated Circle Radius: 7
Colored Circle Radius: 6
Colored Circle Color: blue
In the above code, the child class takes on the radius of the parent circle, without you explicitly defining it. It adds another attribute color
to itself. Every time a negative radius is passed, it will raise an exception for both classes.
Polymorphism
Polymorphism is the ability of a child class to provide a specific implementation for a method already defined in the parent class. This simply means you can create multiple classes that inherit from a common parent class and have their specific implementations of the parent's methods. An example is as follows:
class Circle:
def __init__(self, radius):
self.radius = radius
def calculate_diameter(self):
return 2 * self.radius
class ColoredCircle(Circle):
def __init__(self, radius, color):
super().__init__(radius)
self.color = color
def calculate_diameter(self):
return 2 * self.radius # Override the method to provide a specific implementation
# Polymorphism in action
circle = Circle(5)
colored_circle = ColoredCircle(4, "red")
# Print results
print("Circle Diameter:", circle.calculate_diameter()) # Uses the Circle implementation
print("Colored Circle Diameter:", colored_circle.calculate_diameter()) # Uses the ColoredCircle implementation
Output:
Circle Diameter: 10
Colored Circle Diameter: 8
In the code snippet above, the Circle
and ColoredCircle
classes provide their specific implementations of the calculate_diameter
method.
Abstraction
Abstraction is the process of simplifying a complex system by representing the essential features while hiding the unnecessary details. The user can only view basic functionalities whereas the internal details are hidden. An example is in the code below:
from abc import ABC, abstractmethod # Import ABC (Abstract Base Class) and abstractmethod from the abc module
class Shape(ABC): # Make Shape an abstract class by inheriting from ABC
def __init__(self):
super().__init__()
@abstractmethod
def area(self): # Declare area as an abstract method
pass
class Square(Shape):
def __init__(self, side_length):
super().__init__()
self.side_length = side_length
def area(self):
return self.side_length ** 2
class Circle(Shape):
def __init__(self, radius):
super().__init__()
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
# Create objects
square = Square(4)
circle = Circle(6)
# Calculate and print the areas of the shapes
print("Area of square:", square.area())
print("Area of square:", circle.area())
Output:
Area of square: 16
Area of circle: 113.04
In the code snippet above, we import ABC
and abstractmethod
to create the shape
class. We have made the area method abstract by adding the @abstractmethod
decorator. The code allows you to work with the abstract concept of a shape and its area without worrying about the specific details of how the area is calculated for each shape.
Practical example
A practical example will help you solidify your grasp of the concepts and methods of OOP. In this section, you will see how you can create a simple online shopping system. Customers can add/remove products to their shopping carts and even make a purchase.
To solve this problem, you need to identify the data you need, the actors, and the type of interaction taking place. This information will give you an idea of what attributes you need for your class(es).
Solution:
# Creating 1st class: Customer
class Customer:
def __init__(self, customer_id, name, email, address):
self.customer_id = customer_id
self.name = name
self.email = email
self.address = address
self.shopping_cart = ShoppingCart()
def view_products(self):
# To view available products
pass
def add_to_cart(self, product):
self.shopping_cart.add_item(product)
def remove_from_cart(self, product):
self.shopping_cart.remove_item(product)
def checkout(self):
total = self.shopping_cart.calculate_total()
return total
# Creating 2nd class: Product
class Product:
def __init__(self, product_id, name, price, description):
self.product_id = product_id
self.name = name
self.price = price
self.description = description
# Creating 3rd class: ShoppingCart
class ShoppingCart:
def __init__(self):
self.items = []
def add_item(self, product):
self.items.append(product)
def remove_item(self, product):
if product in self.items:
self.items.remove(product)
def calculate_total(self):
total = sum(product.price for product in self.items)
return total
# Creating objects
customer1 = Customer(1, "Toria", "toria@example.com", "15 Round St")
product1 = product (101, "Top", 5000, "Crop top")
product2 = product (102, "Shoe", 15000, "Boots")
# To interact with the program in your terminal
while True:
print("Available actions:")
print("1. Add a product to your cart")
print("2. Remove a product from your cart")
print("3. View and checkout your cart")
print("4. Exit")
choice = input("Select an action (1/2/3/4): ")
if choice == '1':
print("Available products:")
print("1. Top - #5000")
print("2. Shoe - #15000")
product_choice = input("Select a product (1/2): ")
if product_choice == '1':
customer1.add_to_cart(product1)
elif product_choice == '2':
customer1.add_to_cart(product2)
else:
print("Invalid choice.")
elif choice == '2':
# Add code to remove a product from the cart
pass
elif choice == '3':
total = customer1.checkout()
print(f"Your cart total is: #{total}")
print("Thank you for shopping with us!")
break
elif choice == '4':
print("Exiting the program.")
break
else:
print("Invalid choice. Please select a valid action.")
Output:
Available actions:
1. Add a product to your cart
2. Remove a product from your cart
3. View and checkout your cart
4. Exit
Select an action (1/2/3/4): 1
Available products:
1. Top - #5000
2. Shoe - #15000
Select a product (1/2): 2
Available actions:
1. Add a product to your cart
2. Remove a product from your cart
3. View and checkout your cart
4. Exit
Select an action (1/2/3/4): 1
Available products:
1. Top - #5000
2. Shoe - #15000
Select a product (1/2): 1
Available actions:
1. Add a product to your cart
2. Remove a product from your cart
3. View and checkout your cart
4. Exit
Select an action (1/2/3/4): 3
Your cart total is: #20000
Thank you for shopping with us!
N/B: You can interact with the code in your terminal.
The preceding code provides a simple interactive menu. It allows you to add products to your shopping cart and complete your purchase. It continues to loop until you select the "view and checkout" option or the "exit" option.
Conclusion
Object-oriented programming is a valuable paradigm for solving problems by structuring code around objects and their interactions. It provides a clear, organized, and efficient way to solve real-world problems. Using OOP can also enhance your code and make it easy to maintain.
In this article, you have seen how you can apply the different OOP concepts and methods in solving problems. As you continue to explore OOP in Python and with the knowledge from this article, you will see that OOP offers a flexible approach to problem-solving and code management.