Functions
Parameters, return values, scope, and reusable behavior
Parameters and return values
A function names a reusable operation. def creates the function; its body runs when called. Parameters receive the caller's objects. return supplies a result; reaching the end returns None. Printing does not return a value to the caller.
Positional arguments match by position; keyword arguments match by name. Defaults make arguments optional. A * in a signature makes subsequent parameters keyword-only.
def request_headers(base, *, request_id):
result = dict(base)
result["X-Request-ID"] = request_id
return result
defaults = {"Accept": "application/json"}
headers = request_headers(defaults, request_id="r-17")
print(headers["X-Request-ID"]) # r-17
print("X-Request-ID" in defaults) # FalseCopying base prevents changes to its entries. This is a shallow copy: nested mutable values would remain shared. The keyword-only request_id makes the option explicit at the call site.
Scope and callbacks
Local names belong to a call. Functions can read enclosing and module names, but assignment normally creates a local binding. Prefer explicit parameters over changing global state.
Rebinding a parameter does not rebind the caller's variable; mutating its list or dictionary changes the shared object. State whether a function mutates its input or returns independent data.
Functions are values: they can be stored, passed as callbacks, or returned. Sorting keys and transformation APIs use callbacks to accept caller-defined behavior.
Gotchas
Default expressions are evaluated when the definition executes, not on each call:
def tagged(label, tags=[]):
tags.append(label)
return tags
first = tagged("alpha")
second = tagged("beta")
print(first is second) # True
print(first) # ['alpha', 'beta']Both calls use the same default list. For independent storage, default to None and create a list inside the call. If None is a valid supplied value, use a distinct sentinel for omission. Immutable defaults avoid mutation but may need conversion before editing.
Document callback call counts, failures, and ownership. Pass dependencies as arguments so they can be substituted. Extract functions around meaningful rules; excessive fragmentation can make a simple operation harder to follow.
Put it into practice
Implement the exercise in a full-screen workspace, then run its tests.