Maya has pasted the same total formula into four places, and one copy is already wrong. A function gives that formula one name and one home. What should it return when the order is empty?
A function is a named block of code that can receive inputs and return a result. Functions make programs easier to test, reuse, and read. Define one with def, then call it by name. You will also separate calculation from display and validate at the function boundary.
Define and call
The definition creates the function but does not run it. Calling the function with parentheses runs the body. Parameters are the names in the definition; arguments are the values you pass in.
def greet(name):
return f"Hello, {name}!"
print(greet("Maya"))A reusable message
Return a value
return sends a value back to the caller and ends the function. print only displays text; it does not give the caller a usable result. A function with no return statement returns None.
def total(price, quantity):
return price * quantity
result = total(4.5, 2)
print(result)Returning a calculation
Defaults and keyword arguments
A default parameter is used when the caller omits that argument. Parameters with defaults must come after parameters without them. Keyword arguments make calls readable when a function has several parameters.
def total(price, quantity=1):
return price * quantity
print(total(4.5))
print(total(price=4.5, quantity=3))Defaults and named arguments
Return, don't print
A function that prints is hard to reuse; a function that returns can be combined, tested, and formatted by the caller. Keep calculation and presentation separate. Print at the edge of the program, not in the middle of the calculation.
def line_total(qty, price):
return round(qty * price, 2)
subtotal = line_total(2, 4.5) + line_total(1, 3.0)
print(f"subtotal: {subtotal:.2f}")A return value can be composed
Validate at the boundary
Validate inputs where they enter the function, and raise a specific exception with a useful message. The caller can then decide whether to retry, skip the row, or stop. A function that silently returns None hides the failure.
def line_total(qty, price):
if qty <= 0:
raise ValueError("qty must be positive")
if price < 0:
raise ValueError("price must be non-negative")
return round(qty * price, 2)
print(line_total(2, 4.5))
try:
line_total(0, 4.5)
except ValueError as error:
print(type(error).__name__, error)Validate once, fail clearly
Never use a mutable default
Default arguments are evaluated once, when the function is defined. A list default is therefore shared by every call that does not pass its own list. Use None as the default and create a new list inside the function. This is not a style preference; it prevents data from leaking between calls.
def add_item(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
print(add_item("latte"))
print(add_item("tea"))A fresh list for each call
Key points
- def defines a function; parentheses call it.
- return gives a value back; print only displays it.
- Default parameters make optional arguments convenient.
- Keep each function focused on one job.
- Return values are composable; print is a side effect.
- Use None as the default when the real default is a mutable object.
Common mistakes
- Forgetting to call the function, so the definition never runs.
- Using print inside a function when the caller needs a value returned.
- Putting a parameter without a default after one with a default.
- Printing inside a function when the caller needs the value.
- Defining def add(item, basket=[]) and sharing one list across calls.
Write a function that takes a price and quantity and returns the total, then print the result.
Try it yourself
Change the values, predict the output, then run the code. The Python runtime is downloaded the first time you press Run, so the first run can take a few seconds.
Practice this lesson
12 questions with immediate explanations.