Functions, modules, and error handling · Lesson 1 of 3
Functions, parameters, and return values
Design small functions with explicit inputs, outputs, and safe defaults.
Lesson
1. Concept and mental model
A function gives a meaningful name to a reusable operation. Define it with def, accept inputs as parameters, and return a result with return. A function that returns a value is easier to test and compose than one that only prints or changes unrelated global state. Keep a function focused on one responsibility and choose names that describe the result or action.
2. How it works
Python supports positional, keyword-only, and default parameters. Avoid mutable defaults such as items=[]: that list is created once when the function is defined and is then shared across calls. Use None as the default and create a fresh list inside the function. Keyword arguments make call sites clearer when several parameters have similar types. Add type hints and a docstring when they improve the contract for users of the function.
3. Use it safely and effectively
Return early for simple guard conditions to reduce nesting, but keep important validation explicit. Do not catch every exception inside a helper and silently return a plausible result; that hides failure from the caller. Build and test the function around examples and edge cases: empty input, one item, invalid data, and normal data. Practice by writing a function that calculates a subtotal and separately test its behavior.
4. Syntax reference
def subtotal(prices: list[float], tax_rate: float = 0.0) -> float:
return sum(prices) * (1 + tax_rate)
print(subtotal([10.0, 5.0], tax_rate=0.1))5. Guided walkthrough
Give the function the values it needs as parameters, calculate the result locally, and return it to the caller. Try the function with normal, empty, and boundary inputs; a clear contract makes those cases easy to reason about and test.
6. Common mistakes to avoid
- Using a mutable list or dictionary as a default argument and unintentionally sharing it between calls.
- Printing a result inside a helper when callers need to reuse or test that result.
Visual guide
A function contract
-
Step 01
Inputs
Receive explicit parameters.
-
Step 02
Work
Apply one focused operation.
-
Step 03
Result
Return a value or raise a clear error.
Code examples
def subtotal(prices: list[float], tax_rate: float = 0.0) -> float:
return sum(prices) * (1 + tax_rate)
print(subtotal([10.0, 5.0], tax_rate=0.1))
16.5
def add_item(item: str, items: list[str] | None = None) -> list[str]:
result = [] if items is None else items.copy()
result.append(item)
return result
print(add_item('tea'))
['tea']
def split_total(total: int, people: int) -> tuple[int, int]:
return divmod(total, people)
print(split_total(17, 3))
(5, 2)
Key points
- Design small functions with explicit inputs, outputs, and safe defaults.
- Prefer explicit, testable behavior over hidden side effects.
- Validate assumptions at system boundaries and handle failure deliberately.
Check your understanding
Practice
Your learning progress