DEV Community

Cover image for Python Functions and Mini Projects for Beginners (Part 3)
Nelly Mogere
Nelly Mogere

Posted on

Python Functions and Mini Projects for Beginners (Part 3)

Part 3 (final) of a 3-part series. Real code from my Python learning journey, written for beginners.

The Lesson That Made Everything Click

Copying the same code over and over is painful. Functions fixed that for me. A function packages code under a name, so you write it once and reuse it forever. In this final part you will learn functions and arguments, then use them to build two real projects: a shopping receipt generator and a mini bank with a login system.

You do not need Parts 1 and 2 to follow along. The projects use input(), if/else, and while loops, and I explain each part as we go. For deeper detail, Part 1 covers variables and operators, and Part 2 covers loops.

This part covers:

  • Defining functions
  • Positional, default, and keyword arguments
  • return and function pipelines
  • Project 1: Shopping Receipt Generator
  • Project 2: Mini Bank with Login

1. Functions: Reusable Blocks of Code

My first function:

def describe_student(name, city, track):
    print(f"{name} is from {city}, studying {track}")

describe_student("Bob", "Nairobi", "Data Science")
# Output: Bob is from Nairobi, studying Data Science
Enter fullscreen mode Exit fullscreen mode

def tells Python "I am defining a function". The parameters (name, city, track) are placeholders that wait to receive values. The indented block is the body, the code that runs on every call. The last line is the call: it hands the function three values.

Mental model: parameters are the box, arguments are what you put inside. Parameters live in the definition, arguments live in the call.

KEY IDEA

Same function, different inputs, reusable forever. That is the whole point.

2. Three Ways to Pass Arguments

Positional Arguments: Order Matters

def sum_numbers(num1, num2):
    return num1 + num2

print(sum_numbers(8, 7))  # 15
Enter fullscreen mode Exit fullscreen mode

The first value goes to the first parameter, the second to the second. Swapping works for addition, but in general order can completely change meaning: "Bob", "Nairobi", "Data Science" in the wrong order describes a different person.

Default Arguments: Flexible Calls

def sum_numbers(num1=8, num2=7):
    return num1 + num2

print(sum_numbers())        # 15, uses the defaults
print(sum_numbers(20, 30))  # 50, overrides both
Enter fullscreen mode Exit fullscreen mode

Defaults give the function fallback values. Call it with nothing and it uses 8 and 7; pass values and they override. The receipt project uses this exact idea with discount=0.10 and vat=0.16.

Keyword Arguments: Order Does Not Matter

def sum_numbers(num1=8, num2=7):
    return num1 + num2

print(sum_numbers(num2=30, num1=20))  # 50, order swapped and it still works
Enter fullscreen mode Exit fullscreen mode

three types of arguments

Naming each value means order no longer matters: num2=30 finds num2, num1=20 finds num1. Keyword arguments are the most readable style, like reading a sentence.

QUICK CHECK

Positional arguments must come before keyword arguments. sum_numbers(num1=20, 30) raises a SyntaxError.

Why return Changes Everything

These functions use return, not print. print shows a value on the screen and forgets it; return hands the value back so other code can use it. You cannot do math with what print displays, but you can with what return gives you.

def sum_numbers(num1=8, num2=7):
    return num1 + num2

result = sum_numbers(20, 30)
print(result + 100)  # 150, because sum_numbers returned 50
Enter fullscreen mode Exit fullscreen mode

Passing returned values from one function to the next is the foundation of the receipt project.

REMEMBER

print displays. return delivers. That difference makes functions useful beyond the screen.

3. Project 1: Shopping Receipt Generator

This is the mini project I am most proud of. Three small functions, each doing one job, passing values down a pipeline. try/except catches bad input so a typo does not crash the program:

def generate_receipt():
    items_list = []
    total = 0

    while True:
        name = input("Enter item name (or 'quit'): ").strip()
        if name.lower() == "quit":
            break

        while True:  # keep asking until the price is valid
            price_input = input(f"Enter price for '{name}': ").strip()
            try:
                amount = int(price_input)
                break
            except ValueError:
                print("Invalid price! Please enter a whole number.")

        items_list.append((name, amount))
        total += amount

    print("ITEMS PURCHASED:")
    for name, price in items_list:
        print(f"{name:<35} Ksh {price:>6}")

    print(f"Subtotal is:                        Ksh {total:>6}")
    return total


def calc_discount(subtotal_amount, discount=0.10):
    discount_amount = subtotal_amount * discount
    new_total = subtotal_amount - discount_amount
    print(f"Discount (10% Off):               - Ksh {discount_amount:>6.1f}")
    return new_total


def calc_vat(discounted_amount, vat=0.16):
    vat_amount = discounted_amount * vat
    final_amount = discounted_amount + vat_amount
    print(f"VAT (16%):                        + Ksh {vat_amount:>6.1f}")
    print(f"YOUR TOTAL AMOUNT IS:               Ksh {final_amount:>6.1f}")


current_subtotal = generate_receipt()
if current_subtotal > 0:
    current_discounted = calc_discount(current_subtotal)
    calc_vat(current_discounted)
else:
    print("No items entered. Receipt cancelled.")
Enter fullscreen mode Exit fullscreen mode

mini shopping list

Walk through it in four stages:

  1. Collect. The while True loop asks for item names until "quit". The inner loop validates the price: try attempts int(), and except catches the ValueError when the user types letters, printing a friendly message instead of crashing. Valid entries are stored as tuples in items_list and added to total.
  2. Print. The for loop unpacks each tuple. f"{name:<35}" left-aligns the name, f"{price:>6}" right-aligns the price, so the columns line up.
  3. Return. The function ends with return total, sending the value back to the caller.
  4. Pipeline. The main code stores the returned subtotal, passes it to calc_discount, and passes that result to calc_vat. Each function does one job and hands the result to the next.

WHY IT WORKS

Collect, subtotal, discount, VAT, total. Four steps, four clean functions. If a bug appears, you know exactly which function to look at.

Mini-challenge: add a loyalty bonus

MINI-CHALLENGE

Add a 5% loyalty bonus on the final total. One more function, or one extra line in calc_vat. Your choice.

How to try it: solve it on your own first, then compare with the solution below.

Solution (no peeking before you try):

def add_loyalty_bonus(amount, bonus=0.05):
    bonus_amount = amount * bonus
    print(f"Loyalty Bonus (5%):              - Ksh {bonus_amount:>6.1f}")
    return amount - bonus_amount

# inside calc_vat, at the end:
add_loyalty_bonus(final_amount)
Enter fullscreen mode Exit fullscreen mode

Same patterns as before: a default argument, a calculation, a formatted print, a return. New features become copy-and-adapt, not starting from scratch.

4. Project 2: Mini Bank with Login

Everything from the whole series comes together here: variables, operators, if/else, while loops, and break.

correct_username = "admin"
correct_pin = "1234"
balance = 10000

max_attempts = 3
attempts = 0
logged_in = False

# Login gate, a while loop from Part 2
while attempts < max_attempts:
    print("\n=== Mini Bank ===")
    username = input("Username: ")
    pin = input("PIN: ")

    if username == correct_username and pin == correct_pin:
        print(f"\nWelcome back, {username}!")
        logged_in = True
        break
    else:
        attempts += 1
        remaining = max_attempts - attempts
        if remaining > 0:
            print(f"Incorrect username or PIN. {remaining} attempts remaining")
        else:
            print("Too many failed attempts. Account locked.")

# Main menu, only if login succeeded
if logged_in:
    while True:
        print("\n=== Mini Bank ===")
        print("1. Check balance")
        print("2. Deposit")
        print("3. Withdraw")
        print("4. Exit")

        choice = input("Choose Option: ")
        if choice == "1":
            print(f"Balance: Ksh {balance}")
        elif choice == "2":
            amount = float(input("Deposit Amount: "))
            balance += amount  # assignment operator from Part 1
            print(f"Deposited Ksh {amount:,.1f}   New balance: Ksh {balance:,.1f}")
        elif choice == "3":
            amount = float(input("Withdraw Amount: "))
            if amount > balance:
                print("Insufficient funds")
            else:
                balance -= amount
                print(f"Withdrawn {amount:,.1f}  New balance: Ksh {balance:,.1f}")
        elif choice == "4":
            print("Good Bye")
            break
Enter fullscreen mode Exit fullscreen mode

Two sections, two loops:

  1. The login gate. While attempts is under 3, it asks for credentials and checks both with the and operator from Part 1. A correct login sets logged_in and breaks immediately. A wrong login adds to the counter and either warns or locks the account. This is the Part 2 mini-challenge solution, now in a real project.
  2. The menu loop. Only if logged_in is True does the menu run. Each choice routes through if/elif/else: deposit uses +=, withdraw uses -= with a nested if for insufficient funds, and exit breaks the loop to end the program.

mini bank app output

Spot the series in this project:

  • The and operator in the login check, from Part 1
  • The while loop with break in login and menu, from Part 2
  • The += and -= assignment operators, from Part 1
  • The nested if/else for the withdraw check, from Part 2
  • f-string formatting with :,.1f, from Part 1

REAL-WORLD CONNECTION

A login gate with limited attempts and a menu loop are two of the most common features in banking apps. You built a real product pattern.

Series Wrap-Up

I started with print("hello world"). Three articles later I have a mini bank that locks itself after three wrong PINs. That is what learning by building looks like.

My honest advice:

  • Type the code yourself. Copying builds no muscle memory.
  • Break things on purpose. Change values, remove a continue, see what happens.
  • Build tiny projects. A receipt, a ticket, a bank. Small wins keep you going.
  • Do not fear errors. Mine said "Insufficient funds" for weeks. Errors are just the program talking to you.

The Full Series

Your Turn

Which project will you build first, the receipt or the bank? Share your learning tips in the comments. Beginners learn best from each other. Bookmark this series so you can finish it whenever you are ready.

Happy coding.

Top comments (0)