A simple command-line banking system built with pure Python. It supports account creation, deposits, withdrawals, transaction tracking, and account summaries — all without external libraries.
This project demonstrates core Python concepts:
- Functions with input validation and return values
- Dictionaries to represent structured data (accounts, transactions)
- Lists as an in-memory data store
- Loops for searching and iterating
- Exception handling with
try/exceptandValueError
A global list named accounts stores all bank accounts. Each account is a dictionary with the following structure:
{
"name": "Vlada", # Account holder name (str)
"balance": 1000, # Current balance (float)
"transactions": [ # List of transaction records
{"type": "Deposit", "amount": 200},
{"type": "Withdrawal", "amount": 150}
]
}name— Unique identifier for the account holder.balance— The current available balance, updated after every deposit or withdrawal.transactions— A list of dictionaries, each recording thetype("Deposit"or"Withdrawal") andamountof a single operation.
Purpose: Look up an account by name from the accounts list.
How it works:
- Iterates through every account in the global
accountslist. - Performs a case-insensitive comparison (
lower()) so that"vlada"matches"Vlada". - Returns the matching account dictionary, or
Noneif not found.
Why it matters: This is a reusable helper used by every other function to locate accounts before performing operations.
def find_account(name: str):
for account in accounts:
if account["name"].lower() == name.lower():
return account
return NonePurpose: Create a new bank account and add it to the database.
How it works:
- Validates that
initial_balanceis not negative — raisesValueErrorif it is. - Checks for duplicates by calling
find_account(name)— raisesValueErrorif the name already exists. - Builds a new account dictionary with
name,balance, and an emptytransactionslist. - Appends the new account to the global
accountslist. - Returns the created account dictionary.
Key validation logic:
- Negative balance →
ValueError("Initial balance cannot be negative.") - Duplicate name →
ValueError("Account with this name already exists.")
def create_account(name: str, initial_balance: float):
if initial_balance < 0:
raise ValueError("Initial balance cannot be negative.")
if find_account(name):
raise ValueError("Account with this name already exists.")
account = {
"name": name,
"balance": initial_balance,
"transactions": []
}
accounts.append(account)
return accountPurpose: Add money to an existing account.
How it works:
- Validates that
amountis greater than 0 — raisesValueErrorotherwise. - Finds the account using
find_account(name)— raisesValueErrorif not found. - Increases
account["balance"]by the deposit amount. - Records the transaction by appending
{"type": "Deposit", "amount": amount}to the transactions list. - Returns the updated balance.
Example flow:
Balance before: $1000
deposit("Vlada", 200)
Balance after: $1200
Transaction log: [{"type": "Deposit", "amount": 200}]
def deposit(name: str, amount: float):
if amount <= 0:
raise ValueError("Deposit amount must be greater than 0.")
account = find_account(name)
if not account:
raise ValueError("Account not found.")
account["balance"] += amount
account["transactions"].append({"type": "Deposit", "amount": amount})
return account["balance"]Purpose: Remove money from an existing account, with overdraft protection.
How it works:
- Validates that
amountis greater than 0 — raisesValueErrorotherwise. - Finds the account using
find_account(name)— raisesValueErrorif not found. - Checks for sufficient funds — if
amount > balance, raisesValueError("Insufficient funds."). - Decreases
account["balance"]by the withdrawal amount. - Records the transaction by appending
{"type": "Withdrawal", "amount": amount}to the transactions list. - Returns the updated balance.
Overdraft protection: The function will never allow a withdrawal that exceeds the current balance. This prevents the account from going into a negative state.
def withdraw(name: str, amount: float):
if amount <= 0:
raise ValueError("Withdrawal amount must be greater than 0.")
account = find_account(name)
if not account:
raise ValueError("Account not found.")
if amount > account["balance"]:
raise ValueError("Insufficient funds.")
account["balance"] -= amount
account["transactions"].append({"type": "Withdrawal", "amount": amount})
return account["balance"]Purpose: Display a formatted summary of an account and its transaction history.
How it works:
- Finds the account using
find_account(name). - If not found, prints
"Account not found."and returns early. - Prints the account holder's name and current balance.
- Loops through the
transactionslist and prints each one. - If the transactions list is empty, prints
"No transactions yet.".
Sample output:
Account Summary for Vlada
Current Balance: $1050
Transactions:
- Deposit : $200
- Withdrawal : $150
def show_account(name: str):
account = find_account(name)
if not account:
print("Account not found.")
return
print(f"\nAccount Summary for {account['name']}")
print(f"Current Balance: ${account['balance']}")
print("Transactions:")
if not account["transactions"]:
print("No transactions yet.")
else:
for transaction in account["transactions"]:
print(f"- {transaction['type']} : ${transaction['amount']}")Purpose: Verify that all functions work correctly, including error handling.
What it tests:
| Step | Operation | Expected Result |
|---|---|---|
| 1 | create_account("Vlada", 1000) |
Account created with $1000 balance |
| 2 | deposit("Vlada", 200) |
Balance increases to $1200 |
| 3 | withdraw("Vlada", 150) |
Balance decreases to $1050 |
| 4 | withdraw("Vlada", 2000) |
Raises ValueError — insufficient funds |
| 5 | show_account("Vlada") |
Prints account summary with transactions |
Error handling: The try/except block catches the ValueError from the overdraft attempt and prints the error message, allowing the program to continue to the show_account() call.
def run_tests():
try:
create_account("Vlada", 1000)
deposit("Vlada", 200)
withdraw("Vlada", 150)
withdraw("Vlada", 2000) # Overdraft test
except ValueError as error:
print("Error:", error)
show_account("Vlada")
run_tests()python mini_banking_system.pyExpected output:
Error: Insufficient funds.
Account Summary for Vlada
Current Balance: $1050
Transactions:
- Deposit : $200
- Withdrawal : $150
| Concept | Where It's Used |
|---|---|
| Global list | accounts list as a simulated database |
| Dictionaries | Account structure (name, balance, transactions) and transaction records (type, amount) |
| Functions | find_account(), create_account(), deposit(), withdraw(), show_account(), run_tests() |
Loops (for) |
Searching accounts in find_account(), printing transactions in show_account() |
| String methods | .lower() for case-insensitive name matching |
| Exception handling | raise ValueError for validation, try/except in testing |
| Type hints | Function parameter annotations (: str, : float) |
| f-strings | Formatted output in show_account() |