Explore hands-on mini projects focused on encapsulation in Python. Apply object-oriented programming concepts like private attributes, access modifiers, and method control through practical coding challenges and real-world examples.
Objective: Create a BankAccount class that properly encapsulates all account details and provides controlled access through methods.
__account_number (generated automatically)__balance (cannot be accessed directly)__transaction_history (list of transactions)deposit(amount) (with validation for positive amounts)withdraw(amount) (prevent overdrafts)get_balance() (read-only access)view_transactions() (returns a copy of history)@property for account_number (read-only).class BankAccount:
    __next_account_number = 1000  # Class variable for auto-generating account numbers
    def __init__(self, initial_balance):
        self.__account_number = BankAccount.__next_account_number
        BankAccount.__next_account_number += 1
        self.__balance = initial_balance
        self.__transaction_history = []
    # TODO: Add deposit() method with validation
    def deposit(self, amount):
        if amount <= 0:
            print("Amount must be positive!")
        else:
            self.__balance += amount
            self.__transaction_history.append(f"Deposited ${amount}")
    # TODO: Add withdraw() method with overdraft protection
    def withdraw(self, amount):
        if amount <= 0:
            print("Amount must be positive!")
        elif amount > self.__balance:
            print("Insufficient funds!")
        else:
            self.__balance -= amount
            self.__transaction_history.append(f"Withdrew ${amount}")
    # TODO: Add get_balance() (read-only)
    @property
    def balance(self):
        return self.__balance
    # TODO: Add view_transactions() (returns a copy)
    def view_transactions(self):
        return self.__transaction_history.copy()
    # Bonus: Read-only property for account_number
    @property
    def account_number(self):
        return self.__account_number
deposit() and withdraw() methods with validation.get_balance() method using @property.__balance and __transaction_history cannot be modified directly.__transaction_history to prevent external modifications.# Test Case
account = BankAccount(100)
account.deposit(50)
account.withdraw(30)
print(f"Balance: ${account.balance}")  # Should show $120
print(f"Transactions: {account.view_transactions()}")
Balance: $120
Transactions: ['Deposited $50', 'Withdrew $30']
โ
 Private Attributes (__balance, __transaction_history)
โ
 Getter Methods (@property for read-only access)
โ
 Validation (prevent negative deposits/withdrawals)
โ
 Immutable Returns (copy of transaction history)
Bonus Challenge:
transfer(to_account, amount) method that validates balances before transferring.Tutorials, Roadmaps, Bootcamps & Visualization Projects