DEV Community

Neema Kirui
Neema Kirui

Posted on

Python Functions: Write It Once, Use It Everywhere!

At some point every beginner writes the same five lines of code for the third time and thinks, there has to be a better way. There is: a function, a named block of code you write once and run whenever you need it. This post covers defining and calling functions, parameters and arguments, return values, and scope, then shows where they landed in a real project.

Your first function

You create a function with def, give it a name, and indent the code that belongs to it. Nothing runs until you call it by name.

def greet():
    print("Hello, welcome to Python!")

greet()
greet()
Enter fullscreen mode Exit fullscreen mode

The function was written once and ran twice. If the greeting ever changes, you fix it in one place instead of hunting down every copy.

Passing things in

Parameters let you pass information in. In def greet(name), name is the parameter, the placeholder. The value you pass when calling, like "Amina", is the argument.

def greet(name):
    print(f"Hello, {name}!")

greet("Amina")
Enter fullscreen mode Exit fullscreen mode

Parameters can have default values, and you can pass arguments by name. Parameters with defaults have to come after the ones without.

def describe_bundle(name, price, validity="24 hours"):
    print(f"{name} costs Ksh {price} and is valid for {validity}")

describe_bundle("50MB", 5)
describe_bundle("500MB", 50, "7 days")
describe_bundle(name="2GB", price=200, validity="30 days")
Enter fullscreen mode Exit fullscreen mode
50MB costs Ksh 5 and is valid for 24 hours
500MB costs Ksh 50 and is valid for 7 days
2GB costs Ksh 200 and is valid for 30 days
Enter fullscreen mode Exit fullscreen mode

Getting an answer back

Printing shows something on the screen, but it hands nothing back to the rest of your program. return does.

def calculate_total(price, quantity):
    return price * quantity

total = calculate_total(50, 3)
print(total)   # 150
Enter fullscreen mode Exit fullscreen mode

If a function only prints, its result is None, which is why a value that keeps coming back as None usually means you printed where you meant to return. A function can also return several values at once:

def min_max(numbers):
    return min(numbers), max(numbers)

lowest, highest = min_max([4, 9, 1, 7])
print(lowest, highest)   # 1 9
Enter fullscreen mode Exit fullscreen mode

Where a variable lives

Variables created inside a function only exist inside it. Once the function finishes, they're gone. Passing a value in doesn't change the original either, because the function works on its own copy:

def deduct(balance, amount):
    balance = balance - amount
    return balance

balance = 200
deduct(balance, 50)
print(balance)            # 200, unchanged

balance = deduct(balance, 50)
print(balance)            # 150
Enter fullscreen mode Exit fullscreen mode

The first call did the maths but nothing caught the answer. The second stored the returned value back into balance, and that's what actually updated it. So if the new value matters, return it and reassign it.

Putting it to work: a bill calculator

def calculate_bill(price, quantity, discount_rate=0):
    subtotal = price * quantity
    discount = subtotal * discount_rate
    return subtotal - discount

print(f"Ksh {calculate_bill(100, 3):.2f}")          # Ksh 300.00
print(f"Ksh {calculate_bill(100, 3, 0.1):.2f}")     # Ksh 270.00
Enter fullscreen mode Exit fullscreen mode

Same function, two situations, and the discount is optional.

How I used functions in my Bundle Purchase Simulator

I used all of this in my Bundle Purchase Simulator, a menu-driven program inspired by dialling *544#. Instead of one giant block, each job got its own function: main_menu() shows the menu, show_bundles() prints a category, buy_bundle() handles a purchase, and check_balance() reports the balance.

def check_balance(balance):
    print(f"Your current balance is Ksh {balance:.2f}")
Enter fullscreen mode Exit fullscreen mode

The part that made scope and return values click was the balance. buy_bundle() deducts money from it, but a function can't change the outer variable directly. So it returns the new balance, and the main loop catches it:

if choice == "1":
    current_balance = buy_bundle("Data", bundles["Data"], current_balance)
Enter fullscreen mode Exit fullscreen mode

Without that reassignment, every purchase would look successful and the balance would never move. Splitting the program into functions also meant I could test each one on its own before wiring them together, which made bugs far easier to find.

Top comments (0)