Pre-COSMOS Day 10 - Functions, part 1 of 2, an advanced session of 40 slides. It teaches def, parameters, and return in depth, as the way to build the reusable decision helpers that a robot's control loop calls. It covers multiple parameters and the order of arguments, return against print - the hardest topic in the diagnostic - and the None trap, then using a returned value, the fact that code after return is dead, functions calling other functions, boolean helpers, default parameters, returning several values, and local scope. It includes real photos of the robot car and its sensors, hand-built diagrams of the function machine, the data flow, and composition, three checks, three traps, and a fully scaffolded your-turn build of is_obstacle, choose_action, and update_battery. Every snippet was run in real Python with the output copied into the trace tables.
Subject: Python · 71 slides · code lesson
Open the interactive version of this deck · Homework for this lesson
Title
Day 10 · Functions, Part 1 of 2
From rigid scripts to a robot brain made of small, reusable helpers. Today: def, parameters, and the return keyword - in depth.
Objectives
A robot's brain isn't one giant script - it's a stack of small helpers the control loop calls by name. By the end of today you can:
def and call it to run its steps.return to hand a value back, and say exactly how it differs from print.if.Warm-up
Discussion prompt
Before we open Functions as Robot Decision Helpers: without looking back, what was the main idea of Functions: Arguments, Scope & Small Programs, and what could you do by the end of it that you could not do before?
Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.
Answer:
Picks up where Day 10 left off and goes deep on how small functions cooperate into a real program: positional vs keyword arguments and why defaults come last; scope (locals don't leak, parameters are local copies, the global-assign UnboundLocalError) and the clean alternative of pass-in / return-out; the accumulator pattern; functions called inside loops; decomposing a problem into helpers with a main loop; and the random module (with seed for reproducible demos). Culminates in a fully scaffolded your-turn build of Robo-Battle - two robots, decide(hp), a turn loop until one hp hits 0 - plus a randomized-damage stretch.
Concept

On the diagnostic, functions were the hardest topic - one question took over fifteen minutes. So functions get two full days, and we start gently.
The single idea that tripped everyone up: print and return are not the same. Nail that today and the rest of functions falls into place.
Good news: by the end of this deck you'll beat exactly that question - on purpose.
Counterexample
Discussion prompt
On the diagnostic, functions were the hardest topic - one question took over fifteen minutes. So functions get two full days, and we start gently.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Answer:
The single idea that tripped everyone up: print and return are not the same. Nail that today and the rest of functions falls into place.
Concept
Three stops, each one a building block for the next:
def, then run it with name().is_obstacle, choose_action, update_battery.Matching
Match the pairs
From Today's roadmap — match each one to what it actually does. The descriptions have been shuffled.
def, then run it with name().is_obstacle, choose_action, update_battery.Why: Define & call, return ≠ print, Robot helpers are easy to tell apart while they are sitting next to their descriptions and much harder afterwards, which is what this checks.
Section
Section 1
Concept
A function bundles several lines of code under one name. You write it once with def; later you call it whenever you need those steps - no retyping.
def — The keyword that defines a function. The indented lines under it are the body - they don't run until the function is called by name with parentheses.
Worked example
Defining a function runs nothing. The body only runs when you call it with ().
def beep():
print("beep")
beep()Line 4 is the call. The parentheses are what makes it run.
| line that runs | what happens |
|---|---|
| def beep(): ... | defines the helper - nothing prints |
| beep() | beep |
Intuition
Picture a little machine: something goes in, the machine does its job, and something comes out.
Figure (svg): A value 5 enters a box labeled double and 10 comes out the other side
Parameters are the way in. return is the way out. The rest of today is just filling in that picture for a robot.
Analogy
Discussion prompt
Explain A function is a machine by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Picture a little machine: something goes in, the machine does its job, and something comes out.
Section
Section 2
Concept

A parameter is a named input written in the parentheses. The same helper now behaves differently depending on the value you pass in.
parameter — A variable listed in a function's parentheses. The value you supply when calling - the argument - is copied into it for that one run.
A helper can take more than one parameter. Then order matters - arguments fill the parameters left to right.
Definition probe
Sort into buckets
Every line below is part of the definition of def or of parameter — one or the other, never both. Put each where it belongs.
Worked example
battery and cost are two parameters. The call fills them in order.
def battery_after_drive(battery, cost):
return battery - cost
battery = battery_after_drive(100, 15)
print(battery)100 goes into battery, 15 goes into cost, so it returns 100 - 15.
| call | battery - cost | returns |
|---|---|---|
| battery_after_drive(100, 15) | 100 - 15 | 85 |
| battery_after_drive(85, 20) | 85 - 20 | 65 |
| battery_after_drive(65, 30) | 65 - 30 | 35 |
Comparison
Comparison matrix
From Two parameters: battery after a drive: refill the returns column from what you know. The rest of the table is as it appeared.
| call | battery - cost | returns |
|---|---|---|
| battery_after_drive(100, 15) | 100 - 15 | 85 |
| battery_after_drive(85, 20) | 85 - 20 | 65 |
| battery_after_drive(65, 30) | 65 - 30 | 35 |
Anomaly
Predict first
A student writes this, and it looks reasonable:
You meant 100 battery, 15 cost - but typed them backwards.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: Python copies 15 into battery and 100 into cost - position decides, not what you meant.
Match the same order as the def: battery first, then cost.
Why: Python copies 15 into battery and 100 into cost - position decides, not what you meant.
Trap
You meant 100 battery, 15 cost - but typed them backwards.
Call battery_after_drive(15, 100)
Why: Python copies 15 into battery and 100 into cost - position decides, not what you meant.
Returns 15 - 100 = -85
Why: Negative battery. The values were swapped, so the math is backwards.
Match the same order as the def: battery first, then cost.
Call battery_after_drive(100, 15)
Why: 100 → battery, 15 → cost, exactly as defined.
Returns 100 - 15 = 85
Why: Arguments fill parameters left-to-right - keep them lined up.
Section
Section 3
Concept
print is for a human - it puts text on the screen and that's it. return is for the program - it sends a value back to wherever the function was called, so your code can keep using it.
return — Ends the function and hands one value back to the caller. The function call then behaves as if it WERE that value.
Explain it
Discussion prompt
Explain print shows; return hands back to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
print is for a human - it puts text on the screen and that's it. return is for the program - it sends a value back to wherever the function was called, so your code can keep using it.
Picture it
Figure (svg): A function box: an arrow labeled print goes to an eye for the human, an arrow labeled return goes back to a code variable
Discussion prompt
Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.
Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.
Answer:
print is shouting the answer across the room - people hear it, but nobody can hold it. return is handing someone a note - they can keep it and use it later.
Intuition
print is shouting the answer across the room - people hear it, but nobody can hold it. return is handing someone a note - they can keep it and use it later.
Figure (svg): A function box: an arrow labeled print goes to an eye for the human, an arrow labeled return goes back to a code variable
Worked example
is_obstacle returns a boolean - the answer to a yes/no question the robot keeps asking.
def is_obstacle(distance):
return distance < 5
print(is_obstacle(3))distance < 5 is already True or False; return hands that value back.
Trace it across four sensor readings. Predict each one before you read the row.
| distance | distance < 5 ? | returns |
|---|---|---|
| 8 | 8 < 5 → False | False |
| 3 | 3 < 5 → True | True |
| 5 | 5 < 5 → False | False |
| 2 | 2 < 5 → True | True |
Trade off
Comparison matrix
From A helper that returns True/False: every row here is a choice with a cost. Fill the returns column, then say which row you would actually pick and what you give up for it.
| distance | distance < 5 ? | returns |
|---|---|---|
| 8 | 8 < 5 → False | False |
| 3 | 3 < 5 → True | True |
| 5 | 5 < 5 → False | False |
| 2 | 2 < 5 → True | True |
Anomaly
Predict first
A student writes this, and it looks reasonable:
The body prints the test, then we try to store the result.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: It shows True on screen - it looks like it worked.
The body returns the test instead of printing it.
Why: It shows True on screen - it looks like it worked.
Trap
The body prints the test, then we try to store the result.
Body runs print(distance < 5)
Why: It shows True on screen - it looks like it worked.
r = is_obstacle(3), then use r
Why: A function with no return hands back None. So r is None - if r: and any math on r break.
The body returns the test instead of printing it.
Body runs return distance < 5
Why: The True/False value is handed back to the caller.
r = is_obstacle(3), then use r
Why: r is now True - store it, pass it on, or drop it straight into an if.
Break the constraint
Discussion prompt
The rule this trap just fixed:
The True/False value is handed back to the caller.
Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?
Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.
Answer:
It shows True on screen - it looks like it worked.
Concept
The whole point of return: the call becomes the value, so you can use it three ways.
power = boost(7)total = boost(7) + 10if is_obstacle(d): ...A print-only helper gives back None, so none of these work. That's the whole difference.
Worked example
The moment return runs, the function stops. Anything after it never runs - that's dead code.
def get_one():
return 1
print("never runs")
print(get_one())Line 3 is below the return, so Python never reaches it.
| line | does it run? | result |
|---|---|---|
| return 1 | yes | hands back 1, function ends |
| print("never runs") | no | dead code - skipped |
Ranking
Put in order
These are the steps of How to write a robot helper, scrambled. Put them back in order before the next slide shows you.
def and a verb that says what it does.return - use print only when a human needs to see it.Why: This is the order the recipe itself gives. Recalling the sequence without the slide in front of you is the difference between recognising the method and being able to run it — most of what goes wrong in practice is a step done out of turn.
Pattern
Every helper you build today follows the same four moves:
def and a verb that says what it does.return - use print only when a human needs to see it.Edge cases
Discussion prompt
How to write a robot helper works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".
Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.
Answer:
Every helper you build today follows the same four moves:
Elimination
Eliminate the wrong options
What does this print? def is_close(d): print(d < 5) r = is_close(3) print(r)
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: is_close uses print, so it shows True. But it has no return, so it hands back None - r is None. The last line then prints None. Output: True, then None.
Check
This is the diagnostic question in disguise. Work it out on paper first.
Check your understanding
What does this print?
def is_close(d):
print(d < 5)
r = is_close(3)
print(r)
Answer: A
Why: is_close uses print, so it shows True. But it has no return, so it hands back None - r is None. The last line then prints None. Output: True, then None.
Section
Section 4
Picture it
Figure (svg): A large box choose_action contains a smaller box is_obstacle with an arrow between them
Discussion prompt
Read the picture before the words. What is this showing, and what is the one thing it is built to make obvious? Commit to an answer, then read on.
Hint: Name the parts, then say what changes between them — and if nothing changes, say what is being held still.
Answer:
A function can call another function. choose_action doesn't re-check the distance itself - it asks is_obstacle and trusts the answer.
Concept
A function can call another function. choose_action doesn't re-check the distance itself - it asks is_obstacle and trusts the answer.
Figure (svg): A large box choose_action contains a smaller box is_obstacle with an arrow between them
This is how real robot code stays readable: tiny trustworthy helpers, stacked.
Explain it
Discussion prompt
Explain Helpers built from helpers to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
A function can call another function. choose_action doesn't re-check the distance itself - it asks is_obstacle and trusts the answer.
Worked example

Two returns. The first one that runs wins - the function ends right there.
def choose_action(dist):
if is_obstacle(dist):
return "turn"
return "move"If is_obstacle(dist) is True, it returns "turn" and the last line never runs.
| dist | is_obstacle(dist) | returns |
|---|---|---|
| 8 | False | move |
| 3 | True | turn |
| 5 | False | move |
| 2 | True | turn |
Pattern
Step through it
Step through choose_action: decide turn or move one row at a time. What is driving the change, and what would the row after the last one be?
Prediction
Predict first
def choose_action(dist): if is_obstacle(dist): return "turn" return "move" What is choose_action(5)?
Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.
Correct: "move"
Why: is_obstacle(5) is 5 < 5, which is False (5 is not less than 5). The if is skipped, so the function reaches the last line and returns "move".
Check
Remember is_obstacle(d) returns d < 5.
Check your understanding
def choose_action(dist):
if is_obstacle(dist):
return "turn"
return "move"
What is choose_action(5)?
Answer: A
Why: is_obstacle(5) is 5 < 5, which is False (5 is not less than 5). The if is skipped, so the function reaches the last line and returns "move".
Section
Section 5
Concept
Give a parameter a default with =. Then the caller can leave it out and the default fills in.
default parameter — A parameter written like amount=10. If the caller doesn't pass that argument, the default value is used - so the argument becomes optional.
Analogy
Discussion prompt
Explain Default parameters by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Give a parameter a default with =. Then the caller can leave it out and the default fills in.
Worked example
amount defaults to 10. Pass a value to override it; leave it out to use 10.
def charge(battery, amount=10):
return battery + amount
print(charge(50))
print(charge(50, 25))charge(50) uses the default; charge(50, 25) overrides it.
| call | amount used | returns |
|---|---|---|
| charge(50) | 10 (default) | 60 |
| charge(50, 25) | 25 | 75 |
| charge(80) | 10 (default) | 90 |
Pattern
Step through it
Step through charge(battery, amount=10) one row at a time. What is driving the change, and what would the row after the last one be?
Concept
A function can hand back several values at once, separated by commas. The caller can unpack them into several variables.
multiple return — return a, b hands back both values as a pair. Catch them with two names: x, y = f(...).
Matching
Match the pairs
Match each term to the definition this lesson gave it — not the one you would guess from the word.
Why: These are the working definitions of def, parameter, return, default parameter, multiple return as Functions as Robot Decision Helpers uses them. Pairing them correctly is the test of whether you could state each one with the slide switched off.
Worked example
plan calls two helpers and returns both the action and the new battery.
def plan(front, battery):
action = choose_action(front)
battery = update_battery(battery, action)
return action, battery
act, batt = plan(3, 20)
print(act, batt)return action, battery hands back a pair; act, batt catch the two values.
| step | value |
|---|---|
| choose_action(3) | "turn" |
| update_battery(20, "turn") | 19 |
| plan returns | ("turn", 19) |
Comparison
Comparison matrix
From plan(): decide and update at once: refill the value column from what you know. The rest of the table is as it appeared.
| step | value |
|---|---|
| choose_action(3) | "turn" |
| update_battery(20, "turn") | 19 |
| plan returns | ("turn", 19) |
Anomaly
Predict first
A student writes this, and it looks reasonable:
Trying to read a variable that lives inside a function.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: y is a local - it exists only while hide() is running.
return the value out so the caller can keep it.
Why: y is a local - it exists only while hide() is running.
Trap
Trying to read a variable that lives inside a function.
Inside hide(): y = 9
Why: y is a local - it exists only while hide() is running.
After the call, print(y)
Why: NameError: name 'y' is not defined. The local vanished when the function ended.
return the value out so the caller can keep it.
Inside: return y
Why: Hand the value back instead of reaching in for it.
got = hide(); print(got)
Why: Now the value lives in the caller's own variable - 9 prints.
Two truths and a lie
Sort into buckets
Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.
Elimination
Eliminate the wrong options
def charge(battery, amount=10): return battery + amount What does charge(50) return?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: amount isn't passed, so it uses its default of 10. The function returns battery + amount = 50 + 10 = 60.
Check
Predict the return value before you tap.
Check your understanding
def charge(battery, amount=10):
return battery + amount
What does charge(50) return?
Answer: A
Why: amount isn't passed, so it uses its default of 10. The function returns battery + amount = 50 + 10 = 60.
Section
Section 6 · Your turn
Concept
You'll write three helpers, then use them in a two-line decision script. Type every line yourself, run after each one, and read errors - don't erase them.
| # | helper | hands back |
|---|---|---|
| 1 | is_obstacle(distance) | True / False |
| 2 | choose_action(dist) | "turn" / "move" |
| 3 | update_battery(battery, action) | the new battery number |
Counterexample
Discussion prompt
You'll write three helpers, then use them in a two-line decision script. Type every line yourself, run after each one, and read errors - don't erase them.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Worked example
Your turn: write is_obstacle(distance) that hands back True when the wall is closer than 5. Say out loud what is_obstacle(3) should give back.
Hint: you need return (not print), and the test is distance < 5.
def is_obstacle(distance):
return distance < 5| call | returns |
|---|---|
| is_obstacle(3) | True |
| is_obstacle(9) | False |
Worked example
Your turn: write choose_action(dist) that returns "turn" if there's an obstacle, otherwise "move". Reuse Helper 1 - don't re-check the distance yourself.
Hint: if is_obstacle(dist): return "turn" then a plain return "move" below it.
def choose_action(dist):
if is_obstacle(dist):
return "turn"
return "move"| call | returns |
|---|---|
| choose_action(3) | turn |
| choose_action(9) | move |
Worked example
Your turn: write update_battery(battery, action) that costs 2 to "move" and 1 to "turn", and returns the new battery.
Hint: if action == "move": return battery - 2 then return battery - 1.
def update_battery(battery, action):
if action == "move":
return battery - 2
return battery - 1| call | battery change | returns |
|---|---|---|
| update_battery(20, "move") | 20 - 2 | 18 |
| update_battery(20, "turn") | 20 - 1 | 19 |
Trade off
Comparison matrix
From Helper 3 — update_battery: every row here is a choice with a cost. Fill the returns column, then say which row you would actually pick and what you give up for it.
| call | battery change | returns |
|---|---|---|
| update_battery(20, "move") | 20 - 2 | 18 |
| update_battery(20, "turn") | 20 - 1 | 19 |
Worked example
Now use the helpers. Each returned value flows straight into the next line.
front = 3
battery = 20
action = choose_action(front)
battery = update_battery(battery, action)
print(action)
print(battery)choose_action(3) returns "turn"; that feeds update_battery.
| line | value / output |
|---|---|
| choose_action(3) | "turn" |
| update_battery(20, "turn") | 19 |
| print(action) | turn |
| print(battery) | 19 |
If yours prints turn then 19 - you built a robot's decision brain out of functions.
Discrimination
Sort into buckets
Sort these by value / output, from memory, without looking back at Put it together: a tiny robot brain. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.
Worked example
Stretch: write robot_status(battery) that returns "ok" while battery is above 0, else "stranded". Predict robot_status(0).
Hint: one if battery > 0: return "ok", then return "stranded".
def robot_status(battery):
if battery > 0:
return "ok"
return "stranded"
print(robot_status(5))
print(robot_status(0))| call | returns |
|---|---|
| robot_status(5) | ok |
| robot_status(0) | stranded |
Comparison
Comparison matrix
From Stretch — a status helper: refill the returns column from what you know. The rest of the table is as it appeared.
| call | returns |
|---|---|
| robot_status(5) | ok |
| robot_status(0) | stranded |
Connect it up
Draw it
One page, no notation unless you need it: draw how these connect — Define & Call · Parameters - Inputs · return vs print The Big One · Functions Calling Functions · Advanced Power-Ups · Build It: Robot Decision Functions. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.
Recap
return to hand a value back - and explain why it beats print for robot logic.if, or stored in a variable.| move | keyword / form |
|---|---|
| bundle steps under a name | def name(): |
| take inputs | parameters (order matters) |
| hand one answer back | return value |
| hand several back | return a, b |
| optional input | amount=10 |
Next time (Day 11): wire these helpers into the Day 7 while control loop so the robot decides and acts, over and over.
Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.