Master

Designing the parts a program shares

Once a program has several functions, the question is what each one is given and what each one owes. Writing that down is what keeps a growing program from turning into one big knot.

What you will see

0

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

Shape a small program into honest parts.

1

Split input, decision, and output into three functions.

def read(): return [3, 1, 2] def best(items): return max(items) def show(value): print(value) show(best(read()))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
3
2

Chain two functions on one line.

def prepare(text): return text.strip().lower() def count(text): return len(text) print(count(prepare(" OWL ")))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
3
3

Pull the format out of a calculation.

def average(items): return sum(items) / len(items) print("Avg " + str(round(average([1, 2, 3]), 1)))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Avg 2.0
4

Guard the empty case inside the part that divides.

def average(items): if len(items) == 0: return 0 return sum(items) / len(items) print(average([]))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
0
Deciding the empty case where the division happens is the contract; the caller never sees the crash.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

Each function owns one job, is given what it needs, and hands one value to the next function.
1 read gives items
↓
2 best decides
↓
3 show owns output
↓
4 each hand-off is one value
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

def read(): sets nothing on screen
return [3, 1, 2] sets nothing on screen
def best(items): sets nothing on screen
return max(items) sets nothing on screen
def show(value): sets nothing on screen
print(value) 3
show(best(read())) sets nothing on screen
Why this works
Give, do, hand back

Every function should be describable as: given these values, do one job, hand back one value. When you cannot say that sentence, the function needs splitting.

The cost of a good shape

A program of small honest functions can be changed without fear: a function can be replaced, tested, or moved because nothing else reaches into it. That is the payoff, and it is the same rule at every size.

Examples

One owner per job

def read():
    return [3, 1, 2]

def best(items):
    return max(items)

def show(value):
    print(value)

show(best(read()))

read owns input, best owns the decision, show owns output. Each function does one thing and hands over one value.

The value passed on

def prepare(text):
    return text.strip().lower()

def count(text):
    return len(text)

print(count(prepare("  OWL  ")))

prepare returns text that count can use. The shape of the value between them is the whole link.

When a part is too big

def handle(items):
    total = 0
    for n in items:
        total = total + n
    return total / len(items)

This sums and divides in one body. Splitting the sum and the average into two functions makes each one testable alone.

A part that knows too much

def report(items):
    return "Avg " + str(sum(items) / len(items))

This decides the words as well as the number. A reader cannot get the number without the words, so the format belongs one level up.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Functions that change nothing
Previous Lesson
Functions that change nothing
Next Lesson
The contract of a function
The contract of a function →