What Is Object-Oriented Programming In Python

What Is Object-Oriented Programming In Python

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:

ClassObject
Blueprint of a phone.iPhone 6s, Samsung A23
AnimalDog, cat
FoodPasta, 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:

  1. Describe the object: For example, the phone's color or the number of pages in a book.

  2. 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.

References