Python OOPs Concepts

Object-Oriented Programming (OOP) is a paradigm in Python, which structures programs into reusable pieces of code called objects, encapsulating data and behavior. Python supports OOP through classes and objects. Below is a detailed explanation of key OOP concepts:

1. Class and Object

Class

A class is a blueprint for creating objects. It defines a set of attributes and methods that the objects created from the class will have.

class Dog:
    # Attributes and methods defined here
    species = "Canine"  # Class attribute

    def __init__(self, name, age):  # Constructor
        self.name = name  # Instance attribute
        self.age = age    # Instance attribute

Object

An object is an instance of a class. It is a specific realization of the class, with its own unique data.

my_dog = Dog("Buddy", 5) # Create an object
print(my_dog.name) # Output: Buddy
print(my_dog.species) # Output: Canine

2. Encapsulation

Encapsulation, therefore is the bundling of data and methods which operate upon the data together in one unit, typically called the class. Moreover, access to certain details of an object would be controlled through methods.

Example:

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Private attribute

    def deposit(self, amount):
        self.__balance += amount

    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Insufficient funds!")

    def get_balance(self):
        return self.__balance

account = BankAccount(100)
account.deposit(50)
print(account.get_balance())  # Output: 150
account.__balance = 1000  # This won't change the actual balance
print(account.get_balance())  # Output: 150

Key Points:

  • Attributes starting with __ are private.
  • Use methods like get_balance() to access private data.

3. Inheritance

Inheritance allows a class (child) to inherit the attributes and methods of another class (parent), promoting code reuse.

Example:

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        pass  # To be implemented by subclasses

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

dog = Dog("Buddy")
cat = Cat("Kitty")

print(dog.speak())  # Output: Buddy says Woof!
print(cat.speak())  # Output: Kitty says Meow!

Key Points:

  • The Dog and Cat classes inherit from the Animal class.
  • Overriding allows subclasses to define their own behavior for a parent class method.

4. Polymorphism

Polymorphism means “many forms.” It allows the same interface (method name) to behave differently depending on the object.

Example:

def animal_speak(animal):
    print(animal.speak())

dog = Dog("Buddy")
cat = Cat("Kitty")

animal_speak(dog)  # Output: Buddy says Woof!
animal_speak(cat)  # Output: Kitty says Meow!

Key Points:

  • Polymorphism allows a function to operate on different types of objects as long as they implement the required method.

5. Abstraction

Abstraction is the concept of hiding the internal details and showing only the necessary features of an object. It is implemented using abstract classes and methods.

Example:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

dog = Dog()
cat = Cat()

print(dog.speak())  # Output: Woof!
print(cat.speak())  # Output: Meow!

Key Points:

  • Abstract classes cannot be instantiated.
  • Subclasses must implement the abstract methods.

6. Method Overriding

Method overriding allows a subclass to provide a specific implementation of a method that is already defined in its parent class.

Example:

class Parent:
    def show(self):
        print("Parent method")

class Child(Parent):
    def show(self):
        print("Child method")

child = Child()
child.show()  # Output: Child method

7. Method Overloading

Python does not support traditional method overloading (same method name with different parameters). Instead, you can achieve it by providing default arguments or handling variable arguments.

Example:

class Calculator:
    def add(self, a, b, c=0):
        return a + b + c

calc = Calculator()
print(calc.add(2, 3))      # Output: 5
print(calc.add(2, 3, 4))   # Output: 9

8. Multiple Inheritance

Python supports multiple inheritance, allowing a class to inherit from more than one parent class.

Example:

class A:
    def method_a(self):
        print("Method A")

class B:
    def method_b(self):
        print("Method B")

class C(A, B):
    pass

c = C()
c.method_a()  # Output: Method A
c.method_b()  # Output: Method B

Key Points:

  • Be cautious of the Diamond Problem, which Python handles using the Method Resolution Order (MRO).

9. Operator Overloading

Python allows operators to be overloaded so that their behavior can be customized for user-defined objects.

Example:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)

    def __str__(self):
        return f"({self.x}, {self.y})"

p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1 + p2)  # Output: (4, 6)

Benefits of OOP:

  • Modularity: Code can be reused across projects.
  • Abstraction: Simplifies complex systems.
  • Encapsulation: Protects data integrity.
  • Inheritance: Promotes code reuse.
  • Polymorphism: Increases flexibility and maintainability.