Docstrings and functions that use functions
A docstring says what a function takes and returns. Small, documented functions can then call each other, so each rule is written once and every caller shares it.
- Write a docstring that states what a function takes and returns
- Split a job into small functions and call one from another
- Return several results at once as a tuple and unpack them
A function is a promise: give me these values, I'll give you back that. A docstring writes the promise down. It is a string on the first line of the body, usually in triple quotes. Python doesn't print it when the function runs; it stores it as the function's documentation, which editors show when you hover over a call, and help() prints.
Run each example to see what it prints. Running examples doesn't count toward progress.
def line_total(quantity, unit_price, discount_rate=0): """Return the price of one order line after its discount. discount_rate is a fraction: 0.1 means 10% off. """ return quantity * unit_price * (1 - discount_rate) print(line_total(2, 15.99)) print(line_total(1, 51.49, 0.15)) print(line_total.__doc__.splitlines()[0])
With small functions in place, a bigger one can call them instead of repeating their arithmetic. order_total below doesn't know how a line is priced or when shipping is free; it asks line_total and shipping_fee. If the shipping rule changes, one function changes and every caller follows.
A function can also return more than one value by returning a tuple, return subtotal, shipping, which the caller unpacks as in the Lists & Tuples module.
def line_total(quantity, unit_price, discount_rate=0): """Return the price of one order line after its discount.""" return quantity * unit_price * (1 - discount_rate) def shipping_fee(subtotal, free_from=50): """Return 0 for orders of free_from or more, otherwise 5.99.""" if subtotal >= free_from: return 0 return 5.99 def order_total(lines): """Return (subtotal, shipping) for a list of (quantity, unit_price, discount_rate).""" subtotal = 0 for quantity, unit_price, discount_rate in lines: subtotal = subtotal + line_total(quantity, unit_price, discount_rate) return subtotal, shipping_fee(subtotal) subtotal, shipping = order_total([(1, 51.49, 0.15), (2, 15.99, 0)]) print(f"items {subtotal:.2f}, shipping {shipping:.2f}, total {subtotal + shipping:.2f}") subtotal, shipping = order_total([(1, 6.49, 0)]) print(f"items {subtotal:.2f}, shipping {shipping:.2f}, total {subtotal + shipping:.2f}")
When to use it: split a function when it does two jobs you could name separately, or when the same calculation appears twice. Write a docstring for any function someone else (including you next month) will call. Mistakes to avoid:
- A docstring that repeats the name (
"""Line total.""") instead of saying what comes in and what comes out. - Copying a helper's arithmetic into the bigger function instead of calling the helper, so the two drift apart.
- Forgetting to return the helper's result:
line_total(...)on its own line computes a value and throws it away.
- A docstring, the first string in a function's body, states what it takes and returns.
- Small functions that call each other keep each rule in one place.
return a, breturns a tuple the caller can unpack.- Next module: put everything so far together in complete small programs.