Foundation · 45 MINUTES · VERSION 1
Python functions, module imports and validated contracts
Return reusable results, reject boolean minutes, keep imports quiet and preserve caller data.
Reviewed 23 Oct 2026Your setup may differ. Windows, Ubuntu, and optional tool bundles →
What you will learn
- Define typed functions with explicit returned results and validation.
- Import a module without running its demonstration.
- Explain boolean/integer boundaries and repeated-call input preservation.
Theory: signatures describe, checks enforce
A function names a job, accepts arguments and returns a value. Type hints help readers and tools; ordinary Python execution does not automatically enforce those hints. Validate unknown data where it crosses your contract.
Here read_minutes accepts only exact built-in integers from0 through480. Python booleans are a kind of integer for isinstance checks, but True is not accepted as one minute. This deliberate contract uses an exact type check. Other products may accept different numeric types; define that instead of relying on accidental coercion.
The report accepts a list of task dictionaries. Missing or unsupported status counts as unknown; a non-dictionary row rejects the whole report. Results are fresh on every call and the input is not edited. Exceptions signal failure; printing belongs in the demonstration caller.
Worked example: reusable functions in a module
Required: Python3.13+ standard library. Save as py_study_functions.py in a personal practice folder. No PHP, database, server, pip package or paid service is required for this lab. Laragon/XAMPP do not replace the Python interpreter.
"""Pure practice functions; importing this module does not run the demo."""
import json
def read_minutes(value: object) -> int:
if type(value) is not int or not 0 <= value <= 480:
raise ValueError("Use integer minutes from 0 to 480; booleans are not minutes.")
return value
def count_work_states(rows: object) -> dict[str, int]:
if not isinstance(rows, list):
raise ValueError("Provide a list of task objects.")
counts = {"todo": 0, "in_progress": 0, "done": 0, "unknown": 0}
for row in rows:
if not isinstance(row, dict):
raise ValueError("Every task must be an object.")
state = row.get("status")
key = state if isinstance(state, str) and state in counts and state != "unknown" else "unknown"
counts[key] += 1
return counts
if __name__ == "__main__":
print(f"Minutes: {read_minutes(30)}")
rows = [{"status": "todo"}, {"status": "done"}, {"status": None}]
print("Counts: " + json.dumps(count_work_states(rows)))
try:
read_minutes(True)
except ValueError as error:
print(f"Rejected: {error}")
Run and explain the result
Windows PowerShell:
python ./py_study_functions.py
Ubuntu Bash:
python3 ./py_study_functions.py
Minutes: 30
Counts: {"todo": 1, "in_progress": 0, "done": 1, "unknown": 1}
Rejected: Use integer minutes from 0 to 480; booleans are not minutes.
Use a compatible Python interpreter on PATH. Windows may also provide the py launcher; follow the general setup guide rather than assuming a particular bundle. Actual verification used Python3.13 on Windows. Ubuntu execution is not claimed.
Def creates callable functions. Return provides the result to a caller; it is not print. The name guard runs the demo only when this file is executed directly. Importing it provides functions without printing or processing task data. The unhashable list status is checked as text before membership, avoiding a dictionary-key type error.
Guided lab
- Call read_minutes with0,30,480; reject-1,481,True,False,"30",30.0,None and a list. Predict each result before running.
- Import the module from a second file. Confirm no demo output appears. Avoid naming your file json.py, which can shadow the standard library.
- Call count_work_states twice with the same list and compare the input afterward.
- Test empty rows, unknown strings, missing/null/list status and a non-dictionary row after a valid row. The latter must raise rather than return a partial success.
- Catch only an expected error at the calling boundary. Do not turn every exception into a fabricated successful report.
Independent exercise
Write accepted_minutes(values: object) that validates a list and returns a fresh list of valid minute integers. Choose one policy: reject the whole input at the first invalid value, or return a structured accepted/errors result. Document it and never silently drop bad rows.
Test an empty list, zero, boolean, text, out-of-range values, repeated calls and unchanged input. Hint: reuse read_minutes without changing its contract. Keep persistence and display outside this function.
Common mistakes and interview
Avoid treating hints as runtime validation, accepting booleans accidentally, using shared/global counters, printing instead of returning, importing a module that runs a demo, and swallowing failures as completed work.
Interview: “How do you make a calculation reusable and testable?” Explain its contract, pure returned result, validation/errors, no mutation and caller responsibilities. Compare the boolean boundary with strict PHP checks without assuming both languages coerce identically.
English practice and glossary
Parameter: named function input. Argument: a supplied value. Return: give a result to a caller. Module: an importable Python file. Type hint: a documented type expectation. Exception: a failure signal.
Write: “The function accepts __ and returns __. It rejects __ because __. Importing the module does not __.” Record evidence in the private journal.
Recap and next step
Define explicit boundaries and reuse pure functions. Keep module imports quiet and preserve caller data. Next read UTF-8 JSON from a bounded local file and validate decoded shape before processing it.
Reveal exercise hints and self-check answers
Hints alone do not reject data. True is rejected despite Python’s boolean/integer relationship. A non-dictionary task fails; unknown status within a valid dictionary increments unknown. Empty counts start at zero and each call has fresh counters.
Exercise outline: verify outer list, initialize a new result, call read_minutes for each value and follow the documented failure policy. Do not mutate the caller list or write a file.