runtime
45 / 250
lesson 45 Functions · beginner

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.

You'll be able to
  • 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
The idea

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.

example-1.py · A documented line total
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.

example-2.py · An order total built from helpers
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.
Review
  • 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, b returns a tuple the caller can unpack.
  • Next module: put everything so far together in complete small programs.
Practise this

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

Docstrings and functions that use functions — Functions · Runtime