Object Oriented Programming (OOP) in Python 3

Object Oriented Programming (OOP) is a fundamental paradigm in Python that allows you to create modular and maintainable code. OOP focuses on creating objects that encapsulate both data and functionality. In this comprehensive guide, we’ll delve into the basics and advanced concepts of OOP in Python, including classes, objects, inheritance, polymorphism, encapsulation, and data abstraction.  

oops-in-python

Table of Contents


Introduction to Object Oriented Programming

Object-Oriented Programming (OOP) is a programming paradigm that uses objects and classes to organize code. It promotes the concept of “objects” as the primary building blocks of programs, allowing for modular and reusable code.  


Classes and Objects


Class in Python

A class is a blueprint for creating objects. It defines properties (attributes) and behaviors (methods) that will be common to all objects created from the class.  

class Dog:  

    def __init__(self, name):  

        self.name = name  

    def bark(self):  

        return "Woof!"  
  
  
# Creating an object of the Dog class  

my_dog = Dog("Buddy")  

# Accessing object attributes and methods  

print(my_dog.name)       # Output: Buddy  

print(my_dog.bark())     # Output: Woof!    


Objects in Python

An object is an instance of a class. It represents a unique entity with its own set of properties and behaviors.  

# Creating another object of the Dog class  

your_dog = Dog("Molly")  

print(your_dog.name)     # Output: Molly  

print(your_dog.bark())   # Output: Woof!  


Polymorphism

Polymorphism is a powerful feature in object-oriented programming that allows objects of different classes to be treated as if they are objects of a common superclass. This means you can use a single interface to operate on objects of different types.  

Let’s consider a simple example with two animal classes, Dog and Cat, both of which have a sound() method:  

class Dog:  

    def sound(self):  

        return "Woof!"  

class Cat:  

    def sound(self):  

        return "Meow!"  

Now, let’s create a function called animal_sound() that takes an animal object as an argument and calls its sound() method:  

def animal_sound(animal):  
  
    return animal.sound()  

Using Polymorphism, We can now create instances of Dog and Cat and pass them to the animal_sound() function:  

dog = Dog()  

cat = Cat()  

print(animal_sound(dog))  # Output: Woof!  

print(animal_sound(cat))  # Output: Meow!  


Key Points:

  • animal_sound() is a polymorphic function because it can accept objects of different classes (Dog and Cat) and call their respective sound() methods without knowing the exact type of the object at compile time. 
  • This example demonstrates how polymorphism allows for more flexible and reusable code by enabling you to write functions that can operate on objects of multiple types. 


Encapsulation

Encapsulation is a fundamental concept in object-oriented programming that involves bundling data (attributes) and methods (functions) that operate on that data into a single unit called a class. By doing so, you can control the access to the data and protect it from unauthorized access and modification.  

Let’s take a look at a straightforward example using a BankAccount class:  

class BankAccount:  

    def __init__(self, balance):  

        self.__balance = balance  # Private attribute  


    def get_balance(self):  

        return self.__balance  # Accessor method  


    def deposit(self, amount):  

        self.__balance += amount  # Modifier method  


Key Points:

  • __balance: This is a private attribute. The double underscores before the attribute name make it inaccessible from outside the class.  
  • get_balance(): This is a public method that allows you to safely access the balance attribute.  
  • deposit(): This is a public method that lets you add money to the account balance.  


Using the BankAccount Class: 

# Creating an object of the BankAccount class with an initial balance of 1000  

account = BankAccount(1000)  

# Accessing the balance using the get_balance method  

print(account.get_balance())  # Output: 1000  


# Depositing 500 into the account  

account.deposit(500)  

# Accessing the balance after the deposit  

print(account.get_balance())  # Output: 1500    

In this example, the encapsulation ensures that the balance attribute can only be accessed and modified through the provided methods (get_balance and deposit). This protects the integrity of the data and makes the code more maintainable and robust.   


Inheritance

Inheritance is a basic idea in object-oriented programming where you make a new class using an already existing class as a starting point. The new class inherits attributes and methods from the existing class, promoting code reuse and establishing a hierarchical relationship between the parent (base) class and the child (derived) class.  

Let’s consider an example with an Animal class as the parent class, and Dog and Cat classes as child classes that inherit from the Animal class:  

class Animal:  

    def speak(self):  

        return "Animal speaks"  


class Dog(Animal):  

    def speak(self):  

        return "Dog barks"  


class Cat(Animal):  

    def speak(self):  

        return "Cat meows"  

In this example:  

  • Parent Class (Animal): The Animal class serves as the base or parent class. It has a method called speak() that returns “Animal speaks”.  
  • Child Classes (Dog and Cat): The Dog and Cat classes are derived from the Animal class using inheritance. They both have their own speak() methods, which override the speak() method of the parent Animal class.  

Using Inheritance: 

We can now create objects of the Dog and Cat classes and call their speak() methods:  

dog = Dog()  

cat = Cat()  

print(dog.speak())  # Output: Dog barks  

print(cat.speak())  # Output: Cat meows    


Key Points: 

  • Inheritance: The Dog and Cat classes inherit the speak() method from the Animal class, allowing them to reuse the method without redefining it.  
  • Method Overriding: Both Dog and Cat classes provide their own implementation of the speak() method, which overrides the method inherited from the Animal class. This demonstrates polymorphism in action, where the same method behaves differently in different classes. 
  • Code Reusability: Inheritance promotes code reusability by allowing you to define common methods and attributes in a base class, which can then be inherited by multiple derived classes.  


Data Abstraction

Data Abstraction is a key principle in object-oriented programming that focuses on hiding the complex implementation details and exposing only the essential features or functionalities. This helps in simplifying the complexity and making the code more understandable and maintainable. In Python, data abstraction can be achived / implemented using abstract classes and methods.  

Let’s explore a practical example to understand data abstraction better. We’ll use abstract classes and methods to create a Shape class and its child classes Circle and Rectangle. Each shape will have its own method to calculate its area.  

from abc import ABC, abstractmethod  

class Shape(ABC):  

    @abstractmethod  

    def area(self):  

        pass  


class Circle(Shape):  

    def __init__(self, radius):  

        self.radius = radius  

    def area(self):  

        return 3.14 * self.radius * self.radius  


class Rectangle(Shape):  

    def __init__(self, width, height):  

        self.width = width  

        self.height = height  

    def area(self):  

        return self.width * self.height    


Understanding Data Abstraction:

  • Abstract Class (Shape): The Shape class is an abstract class that defines an abstract method area(). An abstract method is a method that is defined but doesn’t have any code inside it. It serves as a blueprint for other classes to implement.  
  • Concrete Classes (Circle and Rectangle): The Circle and Rectangle classes are concrete classes that inherit from the Shape class and provide their own implementations for the area() method.  


Using Data Abstraction: 

We can now create objects of the Circle and Rectangle classes and call their area() methods to calculate the areas:  

circle = Circle(7)  

rectangle = Rectangle(5, 10)    

print("Area of Circle:", circle.area())        # Output: Area of Circle: 153.86    

print("Area of Rectangle:", rectangle.area())  # Output: Area of Rectangle: 50  


Key Points:

Abstraction: The Shape class serves as an abstraction that defines the common method area() without providing its implementation. This encourages the child classes to provide their own implementations, adhering to the principle of abstraction.  

Code Reusability: By using abstraction, we can define a common interface (Shape) that can be implemented by multiple classes (Circle and Rectangle). This encourages using code again and makes it easier to maintain. 

Encapsulation: The concept of abstraction is closely related to encapsulation, where we encapsulate the details and provide a simplified interface to interact with the objects. This helps in managing complexity and improving the overall design of the code.  


Conclusion

Object Oriented Programming (OOP) in Python offers a powerful way to structure and organize code. By understanding the core concepts of classes, objects, inheritance, polymorphism, encapsulation, and data abstraction, you can design robust and maintainable Python applications.

Happy Coding!

Copyright © 2024 - All Rights Reserved By Infronx