Understanding Object-Oriented Programming in Python

In this blog post, we'll explore the principles of Object-Oriented Programming (OOP) in Python. We'll cover the basics of classes, objects, inheritance, and encapsulation through examples.

1. Introduction to Classes and Objects

In Python, a class is a blueprint for creating objects. Objects represent instances of classes. Here's a basic example:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

# Creating an object of the Person class
person1 = Person("Alice", 30)
print(person1.greet())  # Output: Hello, my name is Alice and I am 30 years old.

In this example, we define a `Person` class with an `__init__` method that initializes the attributes `name` and `age`, and a `greet` method that returns a greeting message.

2. Inheritance

Inheritance allows a class to inherit attributes and methods from another class. Here's an example of inheritance:

class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)
        self.student_id = student_id

    def student_info(self):
        return f"Student ID: {self.student_id}"

# Creating an object of the Student class
student1 = Student("Bob", 22, "S12345")
print(student1.greet())       # Output: Hello, my name is Bob and I am 22 years old.
print(student1.student_info())  # Output: Student ID: S12345

Here, the `Student` class inherits from the `Person` class and adds a new attribute `student_id` and a method `student_info`.

3. Encapsulation

Encapsulation involves bundling the data (attributes) and methods that operate on the data into a single unit (class) and restricting access to some of the object's components. Here’s an example:

class Account:
    def __init__(self, account_number, balance):
        self.__account_number = account_number  # Private attribute
        self. __balance = balance  # Private attribute

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return f"Deposited {amount}. New balance: {self.__balance}"
        return "Invalid amount"

    def get_balance(self):
        return self.__balance

# Creating an object of the Account class
account = Account("123456789", 1000)
print(account.deposit(500))  # Output: Deposited 500. New balance: 1500
print(account.get_balance()) # Output: 1500

The `Account` class uses private attributes (prefixing with `__`) to restrict direct access to the balance and account number. Methods like `deposit` and `get_balance` provide controlled access to these attributes.