Login Sign Up
Python Classes & OOP
Chapter 27 🟡 Intermediate

Python Classes & OOP

Apply your skills with a real-world coding challenge. Try to solve it yourself first!

Coding Challenge: Secure Bank Transaction Processor

Difficulty Level

Intermediate

Problem Description

Imagine you're building a highly resilient, professional-grade banking application. Your task is for build a BankAccount class that processes an account withdrawal using Object-Oriented Programming (OOP) principles.

Instead of writing simple script that might crash than bad inputs or generic errors, you need to build a class blueprint that gracefully handles failure states documents its own business logic. Guarantees no system resources are left hanging, and you will simply accomplish this by bundling the data with internal methods that use a robust 4-step exception handling architecture (try except else finally), custom exceptions, and developer assertions.

You've got to write a class with the withdraw method that: 1. Validates that the withdrawal amount is actually purely a positive number using assertions to catch developer mistakes early. 2, while attempts to process the transaction inside a try block, raising a custom InsufficientFundsError if the user tries for withdraw more money than they have really. 3. Separates a dangerous transaction logic from the safe success processing (like generating the receipt) by utilizing an else block. 4. Always cleanly shuts down simulated database connection inside the finally block, regardless with whether the code succeeds, fails, or panics.

Input & Output Specifications

  • Input:
  • initial_balance (float or int): Passed into the __init__ method to set up the account.
  • withdrawal_amount (float or int): Passed into the withdraw method.
  • Output:
  • Returns the updated account balance (or the original balance if the transaction failed).
  • Must print specific console messages:
  • error message when an InsufficientFundsError occurs.
  • A receipt/success message when the transaction is successful (only if no exceptions were raised).
  • A cleanup message indicating a database connection is safely closed (must happen every single time).

Starter Code Boilerplate

class InsufficientFundsError(Exception):
    """Custom exception for self-documenting business logic."""
    pass

class BankAccount:
    def __init__(self, initial_balance):
        self.balance = initial_balance

    def withdraw(self, amount):
        # Simulated database connection flag
        db_connection_open = True
        print("Opening database connection...")

        # TODO: Use the assert keyword to crash early if 'amount' is <= 0

        try:
            # TODO: Write the logic to check if the balance is sufficient. 
            # If not, raise the InsufficientFundsError.
            # Otherwise, deduct the amount from self.balance.
            pass

        except InsufficientFundsError as e:
            # TODO: Handle the custom exception gracefully and print an error message.
            pass

        else:
            # TODO: Print a success message / receipt. This runs ONLY if the try block succeeds.
            pass

        finally:
            # TODO: Safely shut down the simulated database connection and print a cleanup message.
            pass

        return self.balance

Hints

  • The __init__ and self synergy: Use self.balance inside your methods so your internal tools can seamlessly talk to data bundled inside the instantiated object.
  • Catching Developer Bugs: Use assert amount > 0 to actively crash the program if a developer mistakenly passes the negative withdrawal amount. Assertions are specifically for debugging during development, not for catching user errors.
  • Custom Exceptions: Using standard built-in errors like ValueError for low balance is too vague. Throwing your custom InsufficientFundsError makes your class immediately self-documenting.
  • The else Superpower: Don't stuff too much code into your try block. Put the deduction math inside the try block but put the safe receipt generation inside optional else block, while it runs only if a try block completes without issues.
  • Closing the Door: Whether the logic succeeds, fails, or panics the finally block runs no matter what; use it to set db_connection_open = False and print your simulated cleanup message ensuring your object safely manages resources without memory leaks.

Test Cases

Use the following test cases to verify your class works correctly.

Test Case 1: Successful Transaction * Input: account = BankAccount(100) followed by account.withdraw(40) * Expected Output: Deducts 40 prints the success receipt out of else block, prints database closed message than a finally block, and returns 60.

Test Case 2: Insufficient Funds * Input: account = BankAccount(50), followed by account.withdraw(100) * Expected Output: Triggers the custom InsufficientFundsError, prints the exact error message from the except block, prints the database closed message from the finally block, and returns an original balance of 50.

Test Case 3: Developer Error (Negative Amount) * Input: account = BankAccount(100) followed by account.withdraw(-20) * Expected Output: The program violently crashes with an AssertionError before the try block even starts, indicating a developer tried for pass bad data.

Loading sandbox workspace environment...

Verify Your Solution

Write your solution in the compiler, run it to verify output, then click below to verify.

Learn Together
Session active! Discuss with other learners.
No notes yet. Select text in the concept body to add a note.