ATM Machine Program in Python
Intermediate · Useful tools · about 45 minutes to build · 125 lines
Sign in with a PIN, then check the balance, deposit money, withdraw cash and print a mini statement, as at a real cash machine.
The program
Press Run and answer its questions in the box that appears under the code.
Open it in the full editor to save it, share it and keep building.
Sample run
What the program printed, and what was typed (in yellow).
Welcome to Python Bank (Demo card: the PIN is 1234) Enter your 4-digit PIN: 1111 Wrong PIN. 2 tries left. Enter your 4-digit PIN: 1234 Hello, Asha. 1) Balance 2) Deposit 3) Withdraw 4) Mini statement 5) Exit Choose 1-5: 1 Your balance is 10,000. 1) Balance 2) Deposit 3) Withdraw 4) Mini statement 5) Exit Choose 1-5: 2 Amount to deposit: 2500 Deposited 2,500. New balance: 12,500 1) Balance 2) Deposit 3) Withdraw 4) Mini statement 5) Exit Choose 1-5: 3 Amount to withdraw: 1250 This machine only has notes of 100. Please choose a multiple of 100. 1) Balance 2) Deposit 3) Withdraw 4) Mini statement 5) Exit Choose 1-5: 3 Amount to withdraw: 1500 Please take your cash: 1,500. New balance: 11,000 1) Balance 2) Deposit 3) Withdraw 4) Mini statement 5) Exit Choose 1-5: 4 Type Amount Balance Deposit +2,500 12,500 Withdrawal -1,500 11,000 1) Balance 2) Deposit 3) Withdraw 4) Mini statement 5) Exit Choose 1-5: 5 Please take your card. Goodbye.
How it is built
1. The Account class
The account keeps its balance and a list of transactions. withdraw raises ValueError when the balance is too low, so the account can never go below zero whatever the menu does.
class Account: """A bank account with a PIN, a balance and a list of transactions.""" def __init__(self, owner, pin, balance): self.owner = owner self.pin = pin self.balance = balance self.transactions = [] # (type, amount, balance after) def deposit(self, amount): """Add money and record the deposit.""" self.balance += amount self.transactions.append(("Deposit", amount, self.balance)) def withdraw(self, amount): """Take money out and record it; refuse if the balance is too low.""" if amount > self.balance: raise ValueError("Sorry, there is not enough money in your account.") self.balance -= amount self.transactions.append(("Withdrawal", -amount, self.balance))
2. Three tries at the PIN
Each wrong PIN takes one from tries_left, and the loop ends at zero. A PIN that is not four digits is turned away without using up a try.
def check_pin(account): """Give the user MAX_PIN_TRIES chances; return True if the PIN is right.""" tries_left = MAX_PIN_TRIES while tries_left > 0: pin = input("Enter your 4-digit PIN: ").strip() if not (len(pin) == 4 and pin.isdecimal()): print(" A PIN is 4 digits. Please try again.") continue if pin == account.pin: return True tries_left -= 1 if tries_left > 0: word = "try" if tries_left == 1 else "tries" print(f" Wrong PIN. {tries_left} {word} left.") return False
3. Paying out in notes
amount % NOTE_VALUE is 0 only for whole notes, so 1250 is refused when the notes are 100. The try block shows the account's own message when the money is not there.
def withdraw(account): """Pay out cash if the machine and the balance allow it.""" amount = read_amount("Amount to withdraw: ", WITHDRAW_LIMIT) if amount == 0: return if amount % NOTE_VALUE != 0: print(f" This machine only has notes of {NOTE_VALUE}. " f"Please choose a multiple of {NOTE_VALUE}.") return try: account.withdraw(amount) except ValueError as error: print(f" {error}") return print(f" Please take your cash: {amount:,}. New balance: {account.balance:,}")
4. The mini statement
account.transactions[-STATEMENT_SIZE:] takes the last five items, or all of them when there are fewer. The + in :>+10, prints a sign on every amount, so deposits and withdrawals stand apart.
def mini_statement(account): """Print the last few transactions.""" recent = account.transactions[-STATEMENT_SIZE:] if not recent: print(" No transactions yet.") return print(f" {'Type':<12}{'Amount':>10}{'Balance':>10}") for kind, amount, balance in recent: print(f" {kind:<12}{amount:>+10,}{balance:>10,}")
5. The main flow
Without the right PIN, main() returns at once and the menu never appears. Exit is the only way out of the menu loop.
def main(): """Check the PIN, then serve the menu until the user exits.""" account = Account("Asha", DEMO_PIN, STARTING_BALANCE) print("Welcome to Python Bank") print(f"(Demo card: the PIN is {DEMO_PIN})") if not check_pin(account): print("Too many wrong tries. Your card has been kept; please call your bank.") return print(f"Hello, {account.owner}.") while True: print() print(MENU) choice = input("Choose 1-5: ").strip() if choice == "1": print(f" Your balance is {account.balance:,}.") elif choice == "2": amount = read_amount("Amount to deposit: ", DEPOSIT_LIMIT) if amount: account.deposit(amount) print(f" Deposited {amount:,}. New balance: {account.balance:,}") elif choice == "3": withdraw(account) elif choice == "4": mini_statement(account) elif choice == "5": print("Please take your card. Goodbye.") break else: print(" Please choose a number from 1 to 5.")
Make it bigger
- Add a Change PIN option that asks for the old PIN once and the new one twice.
- Keep several accounts in a dictionary keyed by card number.
- Add a daily limit that counts every withdrawal made in the session.
- Pay out the fewest notes for an amount, using notes of 500, 200 and 100.