Top 5 Python Interview Problems on Classes and Objects

5.51K 0 0 0 0

Chapter 5: Implement a Bank Account System with Encapsulation

🧠 Objective

In this chapter, you’ll learn how to:

  • Use encapsulation in Python to protect sensitive data
  • Create private attributes and restrict direct access
  • Use getter/setter methods to safely manage internal data
  • Implement real-world logic like deposits and withdrawals

Interviewers use problems like this to test your understanding of data hiding, access control, and encapsulated logic, which are fundamental in designing secure and maintainable systems.


📌 Problem Statement

Build a BankAccount class with the following requirements:

  1. Private attributes:
    • __account_holder
    • __balance
  2. Public methods:
    • deposit(amount)
    • withdraw(amount)
    • get_balance() (getter)
    • set_holder(name) (setter)
  3. Withdrawals should not allow the balance to go below zero
  4. Use a __str__() method to show the account info (without exposing private details)

🔧 Step-by-Step Implementation


Step 1: Create the Class with Private Variables

class BankAccount:

    def __init__(self, holder_name, initial_balance=0):

        self.__account_holder = holder_name

        self.__balance = initial_balance

  • The __ prefix makes variables private
  • These cannot be accessed directly from outside the class

Step 2: Add Getter and Setter Methods

    def get_balance(self):

        return self.__balance

 

    def set_holder(self, new_holder):

        if isinstance(new_holder, str) and new_holder.strip():

            self.__account_holder = new_holder

        else:

            raise ValueError("Invalid account holder name.")

This enforces controlled access to the internal state


Step 3: Add Deposit and Withdraw Logic

    def deposit(self, amount):

        if amount > 0:

            self.__balance += amount

        else:

            raise ValueError("Deposit must be a positive amount.")

 

    def withdraw(self, amount):

        if amount > self.__balance:

            raise ValueError("Insufficient funds.")

        if amount <= 0:

            raise ValueError("Withdrawal must be positive.")

        self.__balance -= amount

Checks and balances to avoid negative transactions


Step 4: Add __str__ for Output

    def __str__(self):

        return f"Account Holder: {self.__account_holder}, Balance: ${self.__balance:.2f}"


Full Working Code

class BankAccount:

    def __init__(self, holder_name, initial_balance=0):

        self.__account_holder = holder_name

        self.__balance = initial_balance

 

    def get_balance(self):

        return self.__balance

 

    def set_holder(self, new_holder):

        if isinstance(new_holder, str) and new_holder.strip():

            self.__account_holder = new_holder

        else:

            raise ValueError("Invalid account holder name.")

 

    def deposit(self, amount):

        if amount > 0:

            self.__balance += amount

        else:

            raise ValueError("Deposit must be a positive amount.")

 

    def withdraw(self, amount):

        if amount > self.__balance:

            raise ValueError("Insufficient funds.")

        if amount <= 0:

            raise ValueError("Withdrawal must be positive.")

        self.__balance -= amount

 

    def __str__(self):

        return f"Account Holder: {self.__account_holder}, Balance: ${self.__balance:.2f}"


🧪 Example Usage

account1 = BankAccount("Alice", 500)

account1.deposit(200)

account1.withdraw(100)

 

print(account1)

print("Balance:", account1.get_balance())

 

account1.set_holder("Alice Cooper")

print(account1)


Output:

Account Holder: Alice, Balance: $600.00

Balance: 600

Account Holder: Alice Cooper, Balance: $600.00


🔐 Encapsulation Concepts Table

Concept

Code Example

Description

Private Attribute

self.__balance

Cannot be accessed directly from outside

Getter Method

get_balance()

Safely retrieves a private value

Setter Method

set_holder()

Validates and updates internal data

Data Hiding

__account_holder not accessible directly

Prevents misuse or accidental modification


🔄 Advanced: Use Python @property Decorators

@property

def balance(self):

    return self.__balance

 

@balance.setter

def balance(self, value):

    raise AttributeError("Balance cannot be set directly.")

This makes access cleaner and Pythonic.


Summary Table

Feature

Purpose

__init__

Initialize private account details

__balance

Private attribute; hidden from external code

deposit()

Adds validated amount to balance

withdraw()

Deducts with check for sufficient funds

get_balance()

Controlled access to sensitive data

set_holder()

Allows name change only with validation



Back

FAQs


1. What is a class in Python?

A class is a blueprint for creating objects. It defines attributes (variables) and methods (functions) that describe the behavior of the objects.

2. What is the difference between class variables and instance variables?

Class variables are shared across all instances of a class, whereas instance variables are unique to each object.

3. How does __init__() work in a class?

__init__() is the constructor method in Python that gets called automatically when a new object is instantiated.

4. What’s the difference between __str__() and __repr__()?

__str__() returns a user-friendly string representation of the object, while __repr__() returns a more technical, unambiguous string for developers.

5. How is inheritance implemented in Python classes?

Python allows a class to inherit from another using (BaseClassName) syntax. The child class gets access to the parent’s attributes and methods.

6. What is encapsulation in Python?

Encapsulation is restricting direct access to some of an object’s components. This is done using private attributes and getter/setter methods.

7. Can a class in Python have multiple constructors?

Python does not support multiple __init__ methods. However, you can use default arguments or @classmethod to simulate multiple constructors.

8. What is polymorphism in OOP with Python?

Polymorphism allows methods to have the same name but behave differently depending on the class or object calling them

9. How are objects and instances different in Python?

There's no real difference. "Object" and "instance" are often used interchangeably. An object is an instance of a class.