Editor

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.

atm_machine.py
"""An ATM: PIN check, balance, deposit, withdraw and a mini statement."""
DEMO_PIN = "1234"
STARTING_BALANCE = 10000
MAX_PIN_TRIES = 3
NOTE_VALUE = 100 # The machine pays out notes of this value only
WITHDRAW_LIMIT = 20000 # The most it pays out at one time
DEPOSIT_LIMIT = 50000
STATEMENT_SIZE = 5
MENU = "1) Balance 2) Deposit 3) Withdraw 4) Mini statement 5) Exit"
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))
# The machine
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
def read_amount(prompt, limit):
"""Ask for a whole amount from 1 to limit; return 0 to cancel."""
while True:
text = input(prompt).strip()
if text == "":
return 0
if text.isdecimal() and 1 <= int(text) <= limit:
return int(text)
print(f" Please type a whole amount from 1 to {limit:,}, or Enter to cancel.")
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:,}")
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,}")
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.")
if __name__ == "__main__":
main()

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.

What it practises