Mental model: Think of a function as a vending machine. You put in specific inputs (money and button press), it performs internal actions you cannot see, and hands back exactly one result. If the machine is broken or empty, it returns an error code instead of a snack.
Why this matters: Real programs are full of machines like this. A till needs to clamp prices so no discount goes below zero. A server needs to validate email addresses before saving them. These functions do the heavy lifting silently, keeping your main logic clean.
Docstrings
def calculate_area(width, height):
"""Return the area of a rectangle in square meters."""
return width * height
calculate_area.__doc__Docstrings use triple quotes. They do not run, but they tell other humans (and tools) what your function does and how to call it. If you forget them, future-you will stare at the code wondering if
width is in inches or meters.Returning early
def validate_age(age):
if age < 0:
return 'invalid'
if age == 0:
return 'newborn'
return 'ok'An
if block that contains a return stops the function immediately. Code after it never runs for that input path. This keeps logic flat and easy to read, avoiding deep nesting of else blocks.Functions calling functions
def is_even(n):
return n % 2 == 0
def count_evens(numbers):
total = 0
for num in numbers:
if is_even(num):
total += 1
return total
count_evens([1, 2, 3, 4])count_evens reuses is_even. Each function has one job.Small functions are easier to test and reuse. If a function is doing two different things, split it into two. Name each function for what it does:
format_price, not do_thing.