Python Encapsulation

Introduction

Encapsulation is one of the fundamental concepts in object-oriented programming (OOP). It refers to the bundling of data (attributes) and methods (functions) that operate on the data into a single unit, or class. Encapsulation helps in restricting access to certain components of an object and can prevent the accidental modification of data.

Key Concepts

Public, Protected, and Private Members

  • Public Members: Accessible from outside the class. They are defined without any leading underscores.
  • Protected Members: Indicated by a single underscore (_). They are meant to be accessed within the class and its subclasses.
  • Private Members: Indicated by a double underscore (__). They are meant to be accessed only within the class.

Syntax

Defining a Class with Encapsulation

class MyClass:
    def __init__(self, public, protected, private):
        self.public = public                # Public attribute
        self._protected = protected         # Protected attribute
        self.__private = private            # Private attribute

    def get_private(self):
        return self.__private

    def set_private(self, value):
        self.__private = value

Example

Let’s consider a simple example of a Car class to demonstrate encapsulation.

Car Class

class Car:
    def __init__(self, brand, model, price):
        self.brand = brand             # Public attribute
        self._model = model            # Protected attribute
        self.__price = price           # Private attribute

    def get_price(self):
        return self.__price

    def set_price(self, price):
        if price > 0:
            self.__price = price
        else:
            print("Invalid price")

# Creating an object
my_car = Car("Toyota", "Corolla", 20000)

Explanation

  1. Public Attribute (brand): Accessible from outside the class directly.
  2. Protected Attribute (_model): Intended to be accessed within the class and its subclasses.
  3. Private Attribute (__price): Only accessible within the class using methods.

Using the Car Class

# Accessing public attribute
print(my_car.brand)  # Output: Toyota

# Accessing protected attribute
print(my_car._model)  # Output: Corolla

# Accessing private attribute using getter method
print(my_car.get_price())  # Output: 20000

# Modifying private attribute using setter method
my_car.set_price(25000)
print(my_car.get_price())  # Output: 25000

# Trying to access private attribute directly (will raise an AttributeError)
try:
    print(my_car.__price)
except AttributeError as e:
    print(e)  # Output: 'Car' object has no attribute '__price'

Real-World Example: Bank Account

Let’s consider a real-world example of a bank account system.

BankAccount Class

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

    def get_balance(self):
        return self.__balance

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

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            return f"Withdrew {amount}. New balance is {self.__balance}."
        else:
            return "Invalid amount or insufficient balance"

# Creating an object of the BankAccount class
account = BankAccount("1234567890", 10000)

Explanation

  1. Private Attributes (__account_number, __balance): These are only accessible within the class using methods.
  2. Public Methods (get_balance, deposit, withdraw): These methods provide controlled access to the private attributes.

Using the BankAccount Class

# Accessing and modifying balance using methods
print(account.get_balance())  # Output: 10000
print(account.deposit(5000))  # Output: Deposited 5000. New balance is 15000.
print(account.get_balance())  # Output: 15000
print(account.withdraw(3000))  # Output: Withdrew 3000. New balance is 12000.
print(account.get_balance())  # Output: 12000

# Trying to access private attribute directly (will raise an AttributeError)
try:
    print(account.__balance)
except AttributeError as e:
    print(e)  # Output: 'BankAccount' object has no attribute '__balance'

Conclusion

Encapsulation in Python helps to protect the internal state of an object and prevents unauthorized access and modification. By using public, protected, and private attributes, you can control the access level of each attribute and method. This not only enhances the security of the code but also makes it more modular and maintainable. The real-world example of a bank account demonstrates how encapsulation can be applied to manage sensitive data effectively.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top