Python · Module 4
Functions and modules
Outcome: write a small reusable function with a clear contract. Prerequisite: Module 3. Profile: Python basics. Time: about 2 hours.
Give a transformation a name
A function packages instructions behind a name. Its parameters describe the values it expects, and its return value is the result it hands back. def minutes_to_hours(minutes): defines a function with one parameter. minutes_to_hours(90) calls it with an argument, 90. Defining a function does not run its body; calling it does.
Separate calculation from presentation. A function that returns 1.5 can be used in another calculation, a message or a chart. A function that only prints 1.5 usually returns None, so its caller has no numeric result to reuse. You can print a returned value at the edge of the program, after the calculation is finished.
A good contract answers three questions: what inputs are allowed, what output is returned, and what happens for invalid or empty input? Our conversion accepts a non-negative number of minutes and returns hours. Negative minutes raise ValueError. We assume the caller supplies a number; we do not claim the function validates every possible Python object.
Worked example
# Profile: Python basics
import math
def minutes_to_hours(minutes):
"""Convert a non-negative number of minutes into hours."""
if minutes < 0:
raise ValueError("minutes must be non-negative")
return minutes / 60
def total_minutes(sessions):
"""Return the sum; an empty collection has total zero."""
return sum(sessions)
print("Hours:", minutes_to_hours(90))
print("Total:", total_minutes([20, 30]))
print("Rounded up:", math.ceil(minutes_to_hours(95)))
# print displays a value; return hands a value back to the caller.
The first call returns 1.5. The second returns 50. The last call uses the standard-library math.ceil function to round 95/60 upward to 2. import math makes the module available under its name; the dot selects its ceil function. A module is a reusable unit of Python code, often a .py file.
The triple-quoted text inside a function is its docstring. It describes purpose and assumptions. It does not enforce the rule: the if and raise statements do. raise ValueError(...) deliberately reports that a value is unacceptable. We introduce it here to make the contract explicit; Module 5 explains how a caller can handle this expected failure.
Local names and returned values
A parameter and names assigned inside a function normally belong to that function call. This local scope makes small functions easier to reason about. Avoid making a calculation depend on a changeable global variable when you can pass that input explicitly.
For example, prefer a function taking both quantity and unit_price_cents over a function that quietly reads a price set somewhere else in the program. The explicit version is easier to test and reuse with a different price.
return finishes the current function call immediately. Code after it in the same path does not run. If execution reaches the end without return, Python returns None. You can return a dictionary to give several results names: {"count": len(values), "total": sum(values)}. For an empty list, the count and sum are both zero; an average needs a different policy because division by zero is undefined.
Trace a call without a hidden dependency
len(values) returns the number of items; sum(values) adds the numeric items and returns zero for an empty collection. They are built-in functions, so no import is needed. In the next example, the caller's price is 100, but the call supplies 30 as the function's price parameter. The call returns 60, and the caller's name still holds 100. Rebinding the caller's price to 500 does not change the result of a later call that explicitly supplies 30.
# Profile: Python basics. A parameter belongs to this call.
price = 100
def quoted_total(quantity, price):
return quantity * price
print("Quoted total:", quoted_total(2, 30))
print("Caller price:", price)
price = 500
print("Same explicit quote:", quoted_total(2, 30))
For the negative-duration test, run minutes_to_hours(-1) separately after your valid cases. Expect a visible ValueError and no numeric result. Do not append it before checks you still want to execute, because an unhandled error stops the run. Module 5 teaches how to catch that expected failure; you do not need to invent try/except yet.
Importing your own code
For an optional local exercise, open examples/time_tools.py and examples/04_local_import.py. The first defines a function without printing anything. The second imports it and prints a result. Run python examples/04_local_import.py from the course folder. Python can find the neighboring module when that script runs.
Do not name your own file math.py, csv.py, numpy.py or pandas.py; it can shadow the real module and create confusing import errors. The browser course uses a single code editor, so keep function definitions and calls in one pasted file. Multi-file import behavior in that lab is not required or claimed.
All required functions take explicit inputs. Default arguments are an optional later topic, not a skill you need to infer from these exercises.
Practice
- 4A: implement
minutes_to_hours, including its negative-input error. Check 0, 30, 90 and -1. - 4B: implement
summarize_minutes(values)returning a count and total. Check empty input and[15, 45]. - 4C: call both functions to summarize
[15, 45]and convert its total to hours. Print the result outside the functions. As an optional local extension, move the functions to your own module and import them.
Use exercises/04_functions.py; the NotImplementedError is a reminder to write the function, not a hidden answer.
Exit check
Explain parameter versus argument and print versus return. Why should the behavior for an empty list be decided before implementation? How would a function depending on an unseen global price become harder to test?
Further reading
Read the separate answers and explanations
Reading progress
11 lessons remain open to guests. Marking a lesson read records reading only; it does not award assessment credit or a certificate.
Device reading marks require browser storage. Reading is always available.
Sign in or create an account to save separate account progress. Your current page is kept.
Your earlier place on this device suggests these lessons. No new lesson is marked read.