Introduction to Object-Oriented Programming

Object-Oriented Programming (OOP) is a programming paradigm that organizes data and behavior into objects. Python supports OOP, allowing developers to create reusable and efficient code using concepts like classes, objects, inheritance, and polymorphism.

Key Concepts of OOP

ConceptDescription
ClassA blueprint for creating objects that defines attributes (variables) and behaviors (methods).
ObjectAn instance of a class that contains data and functions that operate on the data.
EncapsulationRestricting access to data and methods to prevent accidental modification.
InheritanceAllowing a class to inherit attributes and behaviors from another class.
PolymorphismProviding a common interface for different data types or classes.

Examples

1. Creating a Simple Class and Object

In this example, we define a class Car that has attributes like brand and model. We then create an object of this class.

Classes act as blueprints for creating objects. When we define a class, we specify attributes (variables) and behaviors (functions). The __init__ method initializes an object when it is created.

</>
Copy
class Car:
    def __init__(self, brand, model):
        self.brand = brand  # Assigning the brand
        self.model = model  # Assigning the model

    def display_info(self):
        print(f"Car Brand: {self.brand}, Model: {self.model}")

# Creating an object of the Car class
my_car = Car("Toyota", "Corolla")

# Calling the method to display car details
my_car.display_info()

Output:

Car Brand: Toyota, Model: Corolla

Here, self refers to the instance of the class. We pass values “Toyota” and “Corolla” when creating an object, which are assigned to brand and model.

2. Encapsulation: Using Private Attributes

Encapsulation restricts direct access to an object’s attributes. Here, we use a private attribute (prefixing it with __).

Private attributes prevent accidental modification from outside the class. We use getter and setter methods to access and update the value safely.

</>
Copy
class BankAccount:
    def __init__(self, account_holder, balance):
        self.account_holder = account_holder
        self.__balance = balance  # Private attribute

    def deposit(self, amount):
        self.__balance += amount
        print(f"Deposited ${amount}. New balance: ${self.__balance}")

    def get_balance(self):
        return self.__balance

# Creating an account
account = BankAccount("Alice", 1000)

# Depositing money
account.deposit(500)

# Accessing balance
print("Current Balance:", account.get_balance())

Output:

Deposited $500. New balance: $1500
Current Balance: 1500

Here, __balance is private and cannot be accessed directly. We use the get_balance() method to retrieve it.

3. Inheritance: Reusing Code

Inheritance allows a class to derive properties and behaviors from another class. We create a Car class and a SportsCar class that inherits from it.

The SportsCar class inherits all properties of Car and adds new behavior.

</>
Copy
class Car:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def display_info(self):
        print(f"Car Brand: {self.brand}, Model: {self.model}")

class SportsCar(Car):
    def __init__(self, brand, model, top_speed):
        super().__init__(brand, model)  # Calling parent class constructor
        self.top_speed = top_speed

    def show_speed(self):
        print(f"The {self.brand} {self.model} has a top speed of {self.top_speed} km/h.")

# Creating an object of SportsCar
sportscar = SportsCar("Ferrari", "F8", 340)

# Using methods
sportscar.display_info()
sportscar.show_speed()

Output:

Car Brand: Ferrari, Model: F8
The Ferrari F8 has a top speed of 340 km/h.

The SportsCar class extends Car and adds a new method show_speed(). We call super().__init__() to reuse the constructor of Car.

4. Handling Errors in OOP

Errors occur when trying to access private attributes or calling undefined methods. Here’s how we handle them using try-catch.

</>
Copy
class Person:
    def __init__(self, name, age):
        self.name = name
        self.__age = age  # Private attribute

    def get_age(self):
        return self.__age

# Creating an object
person = Person("John", 30)

try:
    print(person.__age)  # Trying to access private attribute
except AttributeError as e:
    print("Error:", e)

# Correct way to access private attribute
print("Person's age:", person.get_age())

Output:

Error: 'Person' object has no attribute '__age'
Person's age: 30

Private attributes cannot be accessed directly. We use a method like get_age() to retrieve its value.