All 24 modules are open from the start — nothing here is
locked, and nothing costs anything. Sign in so your progress, titles and
credentials stay with you, on every device you use.
Free forever, with your Google account. No password, no payment.
Give a piece of work a name and stop repeating yourself
A loop stopped you repeating a line. A function stops you repeating an
idea. Name a piece of work once, and every place that needs it says the
name instead of the work — which means there is one place to fix it, one place
to test it, and a program that reads as a description of what it does rather
than a list of how.
Ready?
1
def Names Some Work; Calling Runs It
Defining a function does not run it. It writes the work down under a name
and waits.
def line_total(quantity, price):
return quantity * price
line_total(3, 1250) # 3750
line_total(price=1250, quantity=3) # the same, by name
quantity and price are
parameters — names the function uses for whatever it is
given. 3 and 1250 are
arguments — the actual values at the call. The
distinction is worth keeping straight because error messages use both
words and mean different things by them.
Arguments go in by position, or by name. Naming them is longer and often
much clearer: send(retry=True) tells the reader something
that send(True) does not.
A parameter can have a default, which makes the argument optional:
A function whose body has a bug in it will define perfectly happily.
The error appears the first time something calls it — which is why an
untested function is not a working one, however carefully it was
written.
Quick check
What is the difference between a parameter and an argument?
2
return Hands a Value Back; print Does Not
This is the confusion that costs beginners the most time, and it is worth
being blunt about.
def total_a(q, p):
print(q * p) # puts it on the screen
def total_b(q, p):
return q * p # hands it back to the caller
x = total_a(3, 1250) # prints 3750, and x is None
y = total_b(3, 1250) # prints nothing, and y is 3750
print is for a person. return is for the rest of
the program. A function that prints has done something you cannot use,
test, add up, or put in a file.
A function with no return hands back None — not
an error, not zero, and not the last value it happened to calculate. So
the mistake surfaces later, somewhere else:
TypeError: unsupported operand type(s) for +: 'int' and
'NoneType'.
Dead end
def f(x): print(x * 2)
The answer reached the screen and nothing else can reach it.
Usable
def f(x): return x * 2
Print it at the call if you want to. Or add it up, or store it.
The rule that follows: calculate in functions, print at the
edges. A function that both works something out and decides how
it should look is two functions in a trench coat.
Quick check
A function ends with print(total) and no return. What does the caller get?
3
Leaving Early, and Handing Back More Than One Thing
return ends the function immediately. That makes the guard
clause from module 2-01 much cleaner inside a function — deal with the
impossible case and leave, so the rest of the body has nothing to
indent past:
def average(values):
if not values:
return 0 # nothing to average; done
return sum(values) / len(values)
Without the early return that becomes an if/else
wrapping the whole body. With three such checks it becomes four levels of
indentation, and the actual work is the hardest thing on screen to find.
A function can hand back several values by returning a tuple — usually
written without brackets, and usually unpacked at the call:
This is the same tuple unpacking from module 2-06, and it is where the
habit pays off. Two or three values is comfortable; past that, a
dictionary usually says more about what each one means.
Quick check
What happens to the lines after a return that runs?
4
What Makes a Function Worth Having
Three things, and none of them is length.
1
It does one job
If the name needs an "and" in it, it is two functions. parse_and_save cannot be tested without a database.
2
The name says what, not how
line_total, not multiply_two_numbers. The caller should not need to know the method.
3
Same input, same output
A function that only reads its parameters and returns a value can be tested in one line. One that reaches out to the clock, the network or a global cannot.
A docstring — a string as the very first thing in the
body — says what the function is for. Tools read it, help()
prints it, and it survives being moved in a way a comment above the
def often does not:
def line_total(quantity, price):
"""Return the cost of one order line, before tax."""
return quantity * price
Write what it returns and anything surprising. Do not write
"""Calculates the line total.""" — the name already said
that, and a docstring that only repeats the name is a line everyone
learns to skip.
The real test
Can you describe what the function does in one sentence, without using
the word "and", and without mentioning where it is called from? If yes,
it will be easy to test, easy to reuse, and easy to leave alone in six
months.
Functions are not for saving typing. They are for giving a name to an
idea so the code above can talk about it.
Quick check
Which name is doing its job?
0 of 9 completed
Real Python runs right here in your browser — nothing to install, nothing
sent to a server. The interpreter downloads once the first time you press
Run, then stays cached.
01
To do
def writes some work down under a name. Nothing runs until
something calls it.
def greet(name): return f"Welcome, {name}"
print(greet("Kenji"))
name is a parameter — the name the function
uses for whatever it is handed. "Kenji" is an
argument — the value at the call.
Your task: write line_total(quantity, price)
which hands back the two multiplied together, then print two calls:
3750 11880
your_code.py
PythonCtrl↵ to run
Hint
One line in the body: return quantity * price. The word return is what makes the answer available to the print outside.
Output
02
To do
This function puts the right number on the screen and hands back
None. So total below is None, and
adding to it raises.
print is for a person. return is for the rest of
the program. A function that prints has produced something you cannot use,
test, add up, or write to a file.
Your task: run it, read the TypeError, then
make the function return instead. The printing belongs at the call:
3750 3760
your_code.py
PythonCtrl↵ to run
Hint
Swap print for return inside the function. The two print() lines at the bottom then have a real number to work with.
Output
03
To do
A parameter with a default becomes optional. Callers who do not care get
the sensible behaviour; callers who do can say so.
Parameters with defaults must come after those without, because otherwise
Python could not tell which value you meant to skip.
Your task: add a discount parameter
defaulting to 0, taken off the line as a proportion. Then
print three calls — without a discount, with ten per cent passed by
position, and with ten per cent passed by name:
3750 3375.0 3375.0
your_code.py
PythonCtrl↵ to run
Hint
def line_total(quantity, price, discount=0): and then return quantity * price * (1 - discount). A discount of 0 leaves the total untouched, which is why 0 is the right default.
Output
04
To do
A function with no return hands back None — not
zero, not an error, and not the last value it happened to calculate.
That is the quiet part. The loud part arrives later, wherever the
None is finally used, in a message that names a type nobody
wrote down.
Your task: run it, read where the error actually appears,
then fix the function so the report prints:
Subtotal: 17880
your_code.py
PythonCtrl↵ to run
Hint
The loop works out the right number and then the function ends without handing it over. One line at the end of the body: return running.
Output
05
To do
return ends the function on the spot. That makes a guard
clause cleaner inside a function than anywhere else: handle the case that
would break everything, leave, and the rest of the body has nothing to
indent past.
def average(values): if not values: return 0 return sum(values) / len(values)
Your task: write average(values) which hands
back 0 for an empty list and the mean otherwise. Print two
calls:
76.25 0
Use an early return rather than wrapping the work in an else.
your_code.py
PythonCtrl↵ to run
Hint
`if not values: return 0` catches the empty list, because an empty list is falsy. Then the last line is the ordinary calculation, unindented.
Output
06
To do
A function returns several values by returning a tuple, usually written
without brackets and unpacked at the call. This is the tuple unpacking
from module 2-06, doing the job it was built for.
Your task: write summarise(values) handing
back the lowest, the highest and the total, unpack the result into three
names, and print them:
54 92 305
your_code.py
PythonCtrl↵ to run
Hint
return min(values), max(values), sum(values) — the commas build a tuple. Then low, high, total = summarise(scores) takes it apart.
Output
07
To do
A function is worth having the moment something calls it more than once —
and a loop is the most common second caller.
Notice what this buys: the rule for what a line costs lives in one place.
Change it and every caller changes with it.
Your task: using the line_total already
written, print one row per line and then the order total:
tea 3750 whisk 11880 cloth 2250 Total 17880
The name is left-aligned in ten characters and the figure right-aligned in
six.
your_code.py
PythonCtrl↵ to run
Hint
Loop over rows, split each on the comma, call line_total(int(quantity), int(price)), print the row, and add the same value to order_total. A row is f"{name:<10}{value:>6}".
Output
08
To do
Functions build on each other. Each one does its own job and trusts the
one below it to do theirs, which is how a program stays readable as it
grows.
Your task: write two functions.
line_total(quantity, price) as before, and
order_total(rows) which walks the rows and uses
line_total for each one. Then print two calls:
17880 250
order_total must not do the multiplication itself. The rule
for what a line costs lives in exactly one place.
your_code.py
PythonCtrl↵ to run
Hint
Start a running total at 0, loop over rows, split each into three, and add line_total(int(quantity), int(price)). Return the running total after the loop.
Output
09
To do
calc tells the reader nothing. A good name says
what you get, not how it is worked out — change the method later
and the name should still be true.
A docstring is a string as the very first thing in the
body. Tools read it and help() prints it, which a comment
above the def cannot claim.
Your task: rename calc to
days_until_renewal and give it a docstring of at least
twenty characters saying what it returns. Then print two calls:
26 0
Do not write a docstring that only repeats the name — say what comes back
and what happens at the edge.
your_code.py
PythonCtrl↵ to run
Hint
Rename the function and both calls. The docstring is a plain string on the first line of the body, before `remaining` — something like "Return whole days left in the billing period, or 0 once it has passed."
Output
The invoice toolkit
To do
Three small functions and one report that uses them. The point of the
exercise is the split: each function does one job, hands its answer back,
and knows nothing about how the report is laid out.
Write these three, with exactly these names:
line_total(quantity, price, discount=0) — the cost of one
line, with the discount taken off as a proportion. Rounded to two decimal
places.
parse_line(row) — takes "tea,3,1250" and hands
back the name, the quantity and the price, with the two numbers converted.
Three values, one return.
order_total(rows, discount=0) — the whole order. It must use
the other two rather than repeating their work, and must hand back
0 for an empty list without touching anything else.
Every one of them needs a docstring of at least twenty characters, and none
of them may print.
Then print the report: one row per line at ten per cent off, then the total.
tea 3375.0 whisk 10692.0 cloth 2025.0 Total 16092.0
Name left-aligned in ten, figure right-aligned in eight.
your_code.py
PythonCtrl↵ to run
Hint
line_total is round(quantity * price * (1 - discount), 2). parse_line splits on the comma and returns name, int(quantity), int(price) — three values, one return. order_total guards the empty list with an early return, then loops calling parse_line and line_total and adds them up. In the report, call parse_line for the name and line_total for the figure; a row is f"{name:<10}{value:>8}".