Questions
What problem does Function Basics help us solve in a small Python program?
What should we predict before running the example?
What value, output, or error should we inspect after changing one line?
Objectives
Run a complete example for function basics in Colab.
Explain the example line by line using plain language.
Change one part of the code and predict the result before running it.
Recognize one common mistake and use the error message as evidence.
Hands-on episode: Function Basics
Defining a function teaches Python a reusable step. Calling a function runs it. Parameters are names inside the definition; arguments are the actual values passed during a call.
We will learn this by running code, not by memorizing a definition first. Open the Colab notebook from the button above, find this section, and run each cell in order. Keep a small note beside the notebook with three columns: prediction , actual result , and what changed .
Example 1.1
Predict the returned value and notice that the function body does not run until the call happens.
def double(number):
result = number * 2
return result
answer = double(6 )
print (answer)
Run the cell once without editing it. If the result is different from your prediction, leave the prediction visible and write one sentence about the difference. That sentence is more useful than a perfect first guess.
Explain Example 1.1
def double(number): defines a function and creates the parameter name number.
The indented body is stored as the function’s instructions; it does not run yet.
double(6) calls the function with the argument 6, so inside the function number is 6.
return result sends the value back to the caller, and answer stores it.
Now explain the example out loud or in a Markdown cell. Use short sentences: “this line creates…”, “this name stores…”, “this output appears because…”. If you cannot explain a line yet, run only the lines above it and inspect the values that exist at that moment.
Challenge 1.1
Replace return result with print(result) and inspect answer. It will be None, because printing shows a value to a person while returning gives a value back to the program.
Show a safe way to approach the challenge
Copy Example 1.1 into a new Colab cell.
Change exactly one value, name, condition, or line.
Write the expected output before running the cell.
Run the cell and compare the actual result with your prediction.
If the result surprises you, undo the change and try a smaller one.
Suggested first move: Replace return result with print(result) and inspect answer.
Debugging checkpoint 1.1
If you get a wrong-argument-count error, compare the parameters in the definition with the arguments in the call. If you get NameError outside the function, remember that local names created inside the function do not automatically exist outside it.
Do not debug by rewriting the whole example. Read the error type or surprising output, inspect the closest value with print(...) or type(...), then change one thing. This is the same routine you will use in larger projects.
Apply it
Write celsius_to_fahrenheit(celsius), call it with three values, print the returned results, and then write one sentence explaining why return is better than print for a converter.
Finish by adding a Markdown cell that answers: What did this example teach me that I can reuse in a project?
Key points
Learn the concept by running a complete, small example first.
Predict before execution so your thinking becomes visible.
Change one thing at a time so cause and effect stay clear.
Treat errors as clues about the exact line or value Python could not handle.
The problem this lesson solves
As programs grow, repeated code becomes hard to read and easy to break. A function gives a name to a reusable block of code. Instead of copying the same steps in many places, you define the steps once and call the function when you need them.
Functions also help you think clearly: each function should answer a small question or perform a small task.
How a function call works
A function call moves information into a named block of code and may send one result back to the caller.
flowchart LR
caller["caller code"] -- arguments --> params["parameters<br/>inside function"]
params --> body["function body"]
body -- return value --> caller
body -. local names stay inside .-> scope["local scope"]
Read every function by asking: what arguments go in, what local names are used, and what value comes back?
1. Defining and calling functions
A function definition starts with def:
def greet():
print ("Hello!" )
This teaches Python a new action named greet. It does not run the action yet. To run it, call the function with parentheses:
Example 1
Predict when Hello! will print.
def greet():
print ("Hello!" )
print ("Before" )
greet()
print ("After" )
Step-by-step explanation
def greet():
Python records the function definition.
The indented body belongs to the function.
The body does not run yet.
print("Before")
greet()
Parentheses mean “call this function now”.
Python jumps into the function body and runs print("Hello!").
print("After")
After the function call finishes, Python continues with the next line.
Common mistakes
Mistake: defining a function but never calling it.
def greet():
print ("Hello!" )
Nothing prints because the function is only defined. Add greet() to call it.
Mistake: forgetting parentheses.
This displays information about the function object. Use greet() to run the function.
Check your understanding
{
const scripts = Array . from (
document . querySelectorAll ("script.fcpython-ojs-quiz-config" )
);
const script = scripts. find (
(node) => node. dataset . fcpythonRendered !== "true"
);
if (! script) {
return html `<div class="fcpython-quiz fcpython-quiz-warning">
Quiz configuration was not found.
</div>` ;
}
script. dataset . fcpythonRendered = "true" ;
const quiz = JSON . parse (script. textContent );
const container = html `<div class="fcpython-quiz"></div>` ;
const title = document . createElement ("h3" );
title. textContent = quiz. title ;
container. appendChild (title);
const instructions = document . createElement ("p" );
instructions. textContent = quiz. instructions ;
container. appendChild (instructions);
const feedbackNodes = [];
quiz. questions . forEach ((question, questionIndex) => {
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = ` ${ questionIndex + 1 } . ${ question. prompt } ` ;
fieldset. appendChild (legend);
question. options . forEach ((option, optionIndex) => {
const label = document . createElement ("label" );
label. className = "fcpython-quiz-option" ;
const input = document . createElement ("input" );
input. type = "radio" ;
input. name = ` ${ quiz. id } - ${ question. id } ` ;
input. value = String (optionIndex);
const text = document . createElement ("span" );
text. textContent = option;
label. appendChild (input);
label. appendChild (text);
fieldset. appendChild (label);
});
const feedback = document . createElement ("p" );
feedback. className = "fcpython-quiz-feedback" ;
feedback. setAttribute ("aria-live" , "polite" );
feedbackNodes. push (feedback);
fieldset. appendChild (feedback);
container. appendChild (fieldset);
});
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
quiz. questions . forEach ((question, questionIndex) => {
const selected = container. querySelector (
`input[name=" ${ quiz. id } - ${ question. id } "]:checked`
);
const feedback = feedbackNodes[questionIndex];
if (! selected) {
feedback. textContent = "Choose an answer before checking." ;
feedback. className = "fcpython-quiz-feedback" ;
return ;
}
const selectedIndex = Number (selected. value );
if (selectedIndex === question. answer_index ) {
correctCount += 1 ;
feedback. textContent = `✅ Correct. ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-correct" ;
} else {
const answer = question. options [question. answer_index ];
feedback. textContent = `❌ Not yet. Correct answer: ${ answer} . ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-incorrect" ;
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
score. textContent = "" ;
});
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
return container;
}
2. Parameters and arguments
A parameter is a name in the function definition. An argument is the actual value supplied when the function is called.
Think of a function as a form:
the parameter is the blank line on the form;
the argument is what you write in the blank.
Example 2
def greet(name):
print (f"Hello, { name} !" )
greet("Ada" )
greet("Grace" )
Step-by-step explanation
def greet(name):
The function needs one piece of information.
Inside the function, that information will be called name.
greet("Ada")
The argument is "Ada".
During this call, name refers to "Ada".
The function prints Hello, Ada!.
greet("Grace")
This is a separate call.
During this call, name refers to "Grace".
Multiple parameters
def describe_pet(name, animal):
print (f" { name} is a { animal} ." )
describe_pet("Milo" , "cat" )
Arguments are matched to parameters by position unless you use keyword arguments:
describe_pet(animal= "dog" , name= "Rex" )
Keyword arguments can make calls easier to read.
Common mistakes
Mistake: passing the wrong number of arguments.
def greet(name):
print (f"Hello, { name} !" )
greet()
Python raises TypeError because greet expected one argument and received zero.
Check your understanding
{
const scripts = Array . from (
document . querySelectorAll ("script.fcpython-ojs-quiz-config" )
);
const script = scripts. find (
(node) => node. dataset . fcpythonRendered !== "true"
);
if (! script) {
return html `<div class="fcpython-quiz fcpython-quiz-warning">
Quiz configuration was not found.
</div>` ;
}
script. dataset . fcpythonRendered = "true" ;
const quiz = JSON . parse (script. textContent );
const container = html `<div class="fcpython-quiz"></div>` ;
const title = document . createElement ("h3" );
title. textContent = quiz. title ;
container. appendChild (title);
const instructions = document . createElement ("p" );
instructions. textContent = quiz. instructions ;
container. appendChild (instructions);
const feedbackNodes = [];
quiz. questions . forEach ((question, questionIndex) => {
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = ` ${ questionIndex + 1 } . ${ question. prompt } ` ;
fieldset. appendChild (legend);
question. options . forEach ((option, optionIndex) => {
const label = document . createElement ("label" );
label. className = "fcpython-quiz-option" ;
const input = document . createElement ("input" );
input. type = "radio" ;
input. name = ` ${ quiz. id } - ${ question. id } ` ;
input. value = String (optionIndex);
const text = document . createElement ("span" );
text. textContent = option;
label. appendChild (input);
label. appendChild (text);
fieldset. appendChild (label);
});
const feedback = document . createElement ("p" );
feedback. className = "fcpython-quiz-feedback" ;
feedback. setAttribute ("aria-live" , "polite" );
feedbackNodes. push (feedback);
fieldset. appendChild (feedback);
container. appendChild (fieldset);
});
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
quiz. questions . forEach ((question, questionIndex) => {
const selected = container. querySelector (
`input[name=" ${ quiz. id } - ${ question. id } "]:checked`
);
const feedback = feedbackNodes[questionIndex];
if (! selected) {
feedback. textContent = "Choose an answer before checking." ;
feedback. className = "fcpython-quiz-feedback" ;
return ;
}
const selectedIndex = Number (selected. value );
if (selectedIndex === question. answer_index ) {
correctCount += 1 ;
feedback. textContent = `✅ Correct. ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-correct" ;
} else {
const answer = question. options [question. answer_index ];
feedback. textContent = `❌ Not yet. Correct answer: ${ answer} . ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-incorrect" ;
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
score. textContent = "" ;
});
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
return container;
}
3. Return values
print() displays a value for a human. return sends a value back to the code that called the function.
This difference is one of the most important function ideas.
Example 3
def add(a, b):
total = a + b
return total
result = add(2 , 3 )
print (result)
Step-by-step explanation
def add(a, b): defines a function with two parameters.
result = add(2, 3) calls the function.
Inside the function, a is 2 and b is 3.
total = a + b creates 5.
return total sends 5 back to the caller.
result = ... stores that returned value in result.
print(result) displays 5.
return stops the function
def sign(number):
if number > 0 :
return "positive"
if number < 0 :
return "negative"
return "zero"
Once Python reaches a return, the function ends. Later lines in the function body do not run for that call.
Common mistakes
Mistake: printing instead of returning.
def add(a, b):
print (a + b)
result = add(2 , 3 )
print (result)
The first print displays 5, but result is None because the function did not return a value. Use return a + b when the caller needs the result.
Check your understanding
{
const scripts = Array . from (
document . querySelectorAll ("script.fcpython-ojs-quiz-config" )
);
const script = scripts. find (
(node) => node. dataset . fcpythonRendered !== "true"
);
if (! script) {
return html `<div class="fcpython-quiz fcpython-quiz-warning">
Quiz configuration was not found.
</div>` ;
}
script. dataset . fcpythonRendered = "true" ;
const quiz = JSON . parse (script. textContent );
const container = html `<div class="fcpython-quiz"></div>` ;
const title = document . createElement ("h3" );
title. textContent = quiz. title ;
container. appendChild (title);
const instructions = document . createElement ("p" );
instructions. textContent = quiz. instructions ;
container. appendChild (instructions);
const feedbackNodes = [];
quiz. questions . forEach ((question, questionIndex) => {
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = ` ${ questionIndex + 1 } . ${ question. prompt } ` ;
fieldset. appendChild (legend);
question. options . forEach ((option, optionIndex) => {
const label = document . createElement ("label" );
label. className = "fcpython-quiz-option" ;
const input = document . createElement ("input" );
input. type = "radio" ;
input. name = ` ${ quiz. id } - ${ question. id } ` ;
input. value = String (optionIndex);
const text = document . createElement ("span" );
text. textContent = option;
label. appendChild (input);
label. appendChild (text);
fieldset. appendChild (label);
});
const feedback = document . createElement ("p" );
feedback. className = "fcpython-quiz-feedback" ;
feedback. setAttribute ("aria-live" , "polite" );
feedbackNodes. push (feedback);
fieldset. appendChild (feedback);
container. appendChild (fieldset);
});
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
quiz. questions . forEach ((question, questionIndex) => {
const selected = container. querySelector (
`input[name=" ${ quiz. id } - ${ question. id } "]:checked`
);
const feedback = feedbackNodes[questionIndex];
if (! selected) {
feedback. textContent = "Choose an answer before checking." ;
feedback. className = "fcpython-quiz-feedback" ;
return ;
}
const selectedIndex = Number (selected. value );
if (selectedIndex === question. answer_index ) {
correctCount += 1 ;
feedback. textContent = `✅ Correct. ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-correct" ;
} else {
const answer = question. options [question. answer_index ];
feedback. textContent = `❌ Not yet. Correct answer: ${ answer} . ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-incorrect" ;
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
score. textContent = "" ;
});
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
return container;
}
4. Scope
Scope describes where a variable name can be used. Names created inside a function are usually local to that function.
Example 4
def calculate_total(price, quantity):
total = price * quantity
return total
print (calculate_total(4 , 3 ))
Inside the function, price, quantity, and total are local names. They exist for the function call. Code outside the function should use the returned value, not reach inside for total.
Common mistake
def calculate_total(price, quantity):
total = price * quantity
return total
calculate_total(4 , 3 )
print (total)
This raises NameError because total was created inside the function. Fix it by storing the returned value:
final_total = calculate_total(4 , 3 )
print (final_total)
Practice
In Colab, write and test a small function:
def dollars_to_cents(dollars):
return dollars * 100
print (dollars_to_cents(5 ))
Then modify one thing at a time:
Change the argument from 5 to 12.
Store the result in a variable before printing.
Add a second parameter for extra cents.
Check your understanding
{
const scripts = Array . from (
document . querySelectorAll ("script.fcpython-ojs-quiz-config" )
);
const script = scripts. find (
(node) => node. dataset . fcpythonRendered !== "true"
);
if (! script) {
return html `<div class="fcpython-quiz fcpython-quiz-warning">
Quiz configuration was not found.
</div>` ;
}
script. dataset . fcpythonRendered = "true" ;
const quiz = JSON . parse (script. textContent );
const container = html `<div class="fcpython-quiz"></div>` ;
const title = document . createElement ("h3" );
title. textContent = quiz. title ;
container. appendChild (title);
const instructions = document . createElement ("p" );
instructions. textContent = quiz. instructions ;
container. appendChild (instructions);
const feedbackNodes = [];
quiz. questions . forEach ((question, questionIndex) => {
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = ` ${ questionIndex + 1 } . ${ question. prompt } ` ;
fieldset. appendChild (legend);
question. options . forEach ((option, optionIndex) => {
const label = document . createElement ("label" );
label. className = "fcpython-quiz-option" ;
const input = document . createElement ("input" );
input. type = "radio" ;
input. name = ` ${ quiz. id } - ${ question. id } ` ;
input. value = String (optionIndex);
const text = document . createElement ("span" );
text. textContent = option;
label. appendChild (input);
label. appendChild (text);
fieldset. appendChild (label);
});
const feedback = document . createElement ("p" );
feedback. className = "fcpython-quiz-feedback" ;
feedback. setAttribute ("aria-live" , "polite" );
feedbackNodes. push (feedback);
fieldset. appendChild (feedback);
container. appendChild (fieldset);
});
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
quiz. questions . forEach ((question, questionIndex) => {
const selected = container. querySelector (
`input[name=" ${ quiz. id } - ${ question. id } "]:checked`
);
const feedback = feedbackNodes[questionIndex];
if (! selected) {
feedback. textContent = "Choose an answer before checking." ;
feedback. className = "fcpython-quiz-feedback" ;
return ;
}
const selectedIndex = Number (selected. value );
if (selectedIndex === question. answer_index ) {
correctCount += 1 ;
feedback. textContent = `✅ Correct. ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-correct" ;
} else {
const answer = question. options [question. answer_index ];
feedback. textContent = `❌ Not yet. Correct answer: ${ answer} . ${ question. explanation } ` ;
feedback. className = "fcpython-quiz-feedback is-incorrect" ;
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
score. textContent = "" ;
});
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
return container;
}
Key points
def defines a function; parentheses call it.
Defining a function does not run its body.
Parameters are names in the definition; arguments are values in the call.
return sends a result back to the caller.
print() displays; it does not return a useful value by itself.
Local variables created inside a function are not automatically available outside.
References
Python Tutorial: defining functions: https://docs.python.org/3/tutorial/controlflow.html#defining-functions
Python Tutorial: more on functions: https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions
Back to top