A useful program needs to remember facts. A study planner remembers a course name and a session length. A shop program remembers prices and quantities. A game remembers a score. Python lets us attach readable names to those values, then use the names in later lines.
By the end of the lesson, you should be able to explain:
What is the difference between a value and a name?
What does Python do with the right side of an assignment?
Why can the same name produce different values at different times?
How can a trace table reveal a mistake before the final output?
1. Begin with values Python can use
A value is a piece of information Python can work with. Enter each example in a separate cell:
A value written directly in source code is called a literal . The characters 45 are an integer literal. The quotation marks in "Python Foundations" tell Python to create a text value. True is Python’s spelling for one of its two boolean values.
For now, concentrate on reading the values rather than memorizing every type. Unit 2 studies numbers, text, booleans, conversion, and input in depth.
Values are not automatically displayed in a program
A notebook often displays the value of the last expression in a cell:
A normal Python file does not display every value it creates. Use print() when displaying a value is part of the program:
print (45 )
print ("Python Foundations" )
Line by line:
Python sees the name print.
The parentheses contain the value to give to print.
The first call displays 45.
The second call displays Python Foundations without the quotation marks. The marks describe the text in source code; they are not part of the text.
A notebook may show the last value in a cell as a convenience. If the output is important to your program, call print() explicitly. Later you will learn that a value can be calculated, stored, returned, or displayed—these are different actions.
Try three different uses
Run this cell:
print ("Session minutes:" )
print (45 )
print (30 + 15 )
All three lines display something, but the values reach print() differently:
the first is a text literal;
the second is an integer literal;
the third is calculated from an expression.
Change only 15 to 20. Predict the final line before running it.
2. Give important values readable names
A literal is useful once. A name makes the value easy to reuse and change.
course = "Python Foundations"
session_minutes = 45
print (course)
print (session_minutes)
The first line is an assignment statement . Read it as:
Bind the name course to the value "Python Foundations".
Do not read = as “is already equal to.” In Python, one equals sign performs assignment.
The second assignment binds session_minutes to 45. The calls to print() then ask Python to look up the value currently bound to each name.
What Python does on each line
Consider a small shop calculation:
notebook_price = 4
notebook_count = 3
subtotal = notebook_price * notebook_count
print (subtotal)
Python handles it in this order:
Create the value 4, then bind notebook_price to it.
Create the value 3, then bind notebook_count to it.
Look up notebook_price and obtain 4.
Look up notebook_count and obtain 3.
Multiply the two values and produce 12.
Bind subtotal to 12.
Look up subtotal and display 12.
The names make the calculation explain itself. Compare it with:
Both versions display 12. Only the first version preserves what 4, 3, and 12 mean.
3. Assignment evaluates the right side first
The assignment pattern is:
Python evaluates the expression on the right before it changes the name on the left.
minutes = 20 + 25
print (minutes)
Python adds 20 + 25, obtains 45, and only then binds minutes to 45.
The right side can use names that already exist:
session_minutes = 45
break_minutes = 10
focused_minutes = session_minutes - break_minutes
print (focused_minutes)
Before running, predict the value of focused_minutes.
Line by line:
session_minutes becomes bound to 45.
break_minutes becomes bound to 10.
Python looks up both names and calculates 45 - 10.
focused_minutes becomes bound to the result, 35.
The final line displays 35.
The diagram follows assignment from the written line to the resulting binding.
flowchart LR
A["focused_minutes = session_minutes - break_minutes"] --> B["Look up session_minutes → 45"]
B --> C["Look up break_minutes → 10"]
C --> D["Evaluate 45 - 10 → 35"]
D --> E["Bind focused_minutes → 35"]
The left side is not read like the right side
This line is valid:
This reversed version is not assignment:
The left side must identify a place Python can bind the result. A number literal cannot become a name.
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 progress = document . createElement ("div" );
progress. className = "fcpython-quiz-progress" ;
const counter = document . createElement ("p" );
counter. className = "fcpython-quiz-counter" ;
counter. setAttribute ("aria-live" , "polite" );
progress. appendChild (counter);
const tabs = document . createElement ("div" );
tabs. className = "fcpython-quiz-steps" ;
tabs. setAttribute ("role" , "tablist" );
tabs. setAttribute ("aria-label" , "Quiz questions" );
progress. appendChild (tabs);
container. appendChild (progress);
const questions = document . createElement ("div" );
questions. className = "fcpython-quiz-questions" ;
const feedbackNodes = [];
const questionPanels = [];
const stepButtons = [];
let currentQuestion = 0 ;
function setStepStatus (questionIndex, status) {
const step = stepButtons[questionIndex];
const question = quiz. questions [questionIndex];
const statuses = {
answered : "answered" ,
correct : "correct" ,
incorrect : "incorrect" ,
unanswered : "not answered" ,
};
step. classList . remove (
"is-answered" ,
"is-correct" ,
"is-incorrect" ,
"is-unanswered"
);
if (status) {
step. classList . add (`is- ${ status} ` );
}
const statusLabel = status ? `, ${ statuses[status]} ` : "" ;
step. setAttribute (
"aria-label" ,
`Question ${ questionIndex + 1 } : ${ question. prompt }${ statusLabel} `
);
}
function showQuestion (questionIndex, focusPanel = false ) {
currentQuestion = Math . max (
0 ,
Math . min (questionIndex, quiz. questions . length - 1 )
);
questionPanels. forEach ((panel, index) => {
panel. hidden = index !== currentQuestion;
});
stepButtons. forEach ((step, index) => {
const isCurrent = index === currentQuestion;
step. classList . toggle ("is-current" , isCurrent);
step. setAttribute ("aria-selected" , String (isCurrent));
step. tabIndex = isCurrent ? 0 : - 1 ;
});
counter. textContent = `Question ${ currentQuestion + 1 } of ${ quiz. questions . length } ` ;
const selected = questionPanels[currentQuestion]. querySelector (
"input[type='radio']:checked"
);
const isLastQuestion = currentQuestion === quiz. questions . length - 1 ;
previous. hidden = currentQuestion === 0 ;
next. hidden = isLastQuestion || ! selected;
check. hidden = ! isLastQuestion;
if (focusPanel) {
questionPanels[currentQuestion]. focus ();
}
}
quiz. questions . forEach ((question, questionIndex) => {
const tabId = ` ${ quiz. id } -question-tab- ${ questionIndex + 1 } ` ;
const panelId = ` ${ quiz. id } -question-panel- ${ questionIndex + 1 } ` ;
const step = document . createElement ("button" );
step. type = "button" ;
step. className = "fcpython-quiz-step" ;
step. id = tabId;
step. textContent = String (questionIndex + 1 );
step. setAttribute ("role" , "tab" );
step. setAttribute ("aria-controls" , panelId);
step. setAttribute ("aria-selected" , "false" );
step. tabIndex = - 1 ;
step. addEventListener ("click" , () => showQuestion (questionIndex));
step. addEventListener ("keydown" , (event ) => {
let destination = null ;
if (event . key === "ArrowRight" ) {
destination = (questionIndex + 1 ) % quiz. questions . length ;
} else if (event . key === "ArrowLeft" ) {
destination =
(questionIndex - 1 + quiz. questions . length ) % quiz. questions . length ;
} else if (event . key === "Home" ) {
destination = 0 ;
} else if (event . key === "End" ) {
destination = quiz. questions . length - 1 ;
}
if (destination !== null ) {
event . preventDefault ();
showQuestion (destination);
stepButtons[destination]. focus ();
}
});
stepButtons. push (step);
tabs. appendChild (step);
setStepStatus (questionIndex, "" );
const panel = document . createElement ("div" );
panel. className = "fcpython-quiz-panel" ;
panel. id = panelId;
panel. setAttribute ("role" , "tabpanel" );
panel. setAttribute ("aria-labelledby" , tabId);
panel. tabIndex = - 1 ;
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = 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);
input. addEventListener ("change" , () => {
setStepStatus (questionIndex, "answered" );
feedbackNodes[questionIndex]. textContent = "" ;
feedbackNodes[questionIndex]. className = "fcpython-quiz-feedback" ;
score. textContent = "" ;
showQuestion (questionIndex);
});
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);
panel. appendChild (fieldset);
questionPanels. push (panel);
questions. appendChild (panel);
});
container. appendChild (questions);
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const previous = document . createElement ("button" );
previous. type = "button" ;
previous. className = "fcpython-quiz-secondary" ;
previous. textContent = "Previous" ;
previous. addEventListener ("click" , () => {
showQuestion (currentQuestion - 1 , true );
});
const next = document . createElement ("button" );
next. type = "button" ;
next. textContent = "Next question" ;
next. addEventListener ("click" , () => {
showQuestion (currentQuestion + 1 , true );
});
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. className = "fcpython-quiz-secondary fcpython-quiz-reset" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
let firstQuestionToReview = null ;
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" ;
setStepStatus (questionIndex, "unanswered" );
if (firstQuestionToReview === null ) {
firstQuestionToReview = questionIndex;
}
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" ;
setStepStatus (questionIndex, "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" ;
setStepStatus (questionIndex, "incorrect" );
if (firstQuestionToReview === null ) {
firstQuestionToReview = questionIndex;
}
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
if (firstQuestionToReview !== null ) {
showQuestion (firstQuestionToReview, true );
}
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
stepButtons. forEach ((_, questionIndex) => {
setStepStatus (questionIndex, "" );
});
score. textContent = "" ;
showQuestion (0 , true );
});
actions. appendChild (previous);
actions. appendChild (next);
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
showQuestion (0 );
return container;
}
4. Trace state instead of guessing
A program’s state is the collection of relevant name-to-value bindings at a particular moment. A trace table records that state after each line.
pages_read = 12
pages_read = pages_read + 5
pages_remaining = 30 - pages_read
print (pages_remaining)
Before running, complete the table:
1
12
not assigned
2
?
not assigned
3
?
?
4
?
?
The completed trace is:
1
12
not assigned
2
17
not assigned
3
17
13
4
17
13
The fourth line displays a value but does not change either binding.
Reassignment changes what a name finds next
This line often surprises people seeing programming for the first time:
pages_read = pages_read + 5
It is not an equation claiming that a number equals itself plus five. It is a sequence of actions:
Look up the current value of pages_read: 12.
Add 5: the result is 17.
Bind pages_read to 17.
Future lookups of pages_read find 17.
Reassignment uses the old binding to calculate a new one.
flowchart LR
A["pages_read → 12"] --> B["Evaluate pages_read + 5"]
B --> C["12 + 5 → 17"]
C --> D["pages_read → 17"]
The earlier line of source code has not changed. The program has moved to a new state.
A calculated value does not update by itself
Predict the output:
price = 5
quantity = 2
total = price * quantity
quantity = 4
print (total)
The output is 10, not 20. total = price * quantity ran only once. Changing quantity later does not make Python revisit earlier assignments.
To refresh the total, calculate it again:
price = 5
quantity = 2
total = price * quantity
quantity = 4
total = price * quantity
print (total)
Now the output is 20.
This behavior matters in notebooks. If you run cells out of order, a result may have been calculated from older values. Restarting and running from the top reveals the true sequence.
5. Names must exist before Python can read them
Python can only look up a name after an executed assignment has created its binding.
course = "Python Foundations"
print (course)
Reverse the order:
print (course_name)
course_name = "Python Foundations"
If no earlier cell created course_name, Python raises:
NameError: name 'course_name' is not defined
The source is grammatically valid, so this is not a SyntaxError. Python understood the instruction but could not find the requested name while running it.
Similar-looking names are still different
session_minutes = 45
print (session_minute)
The assignment uses plural minutes; the lookup uses singular minute. Python does not guess that they were intended to match.
Case also matters:
course = "Python Foundations"
print (Course)
course and Course are two different names.
Find the first inconsistent name
Read this without running:
ticket_price = 12
ticket_count = 3
ticket_total = ticket_price * tickets_count
print (ticket_total)
Which name is inconsistent? The third line asks for tickets_count, but the second line created ticket_count.
Make the smallest repair. Do not rename every variable; change the one use that does not match the established name.
Start with the final error line to learn the error type and missing name. Then inspect the named source line and the assignments above it. Look for spelling, pluralization, capitalization, and execution-order differences.
6. Two names can refer to equal values
Run:
morning_minutes = 30
evening_minutes = 30
print (morning_minutes)
print (evening_minutes)
The two names have the same numeric value, but they represent different facts. Changing one binding does not change the other:
morning_minutes = 30
evening_minutes = 30
morning_minutes = 45
print (morning_minutes)
print (evening_minutes)
The output is:
This is why names describe roles, not only values. A program may need separate names even when their current values happen to match.
Copying a value into another name
planned_minutes = 45
actual_minutes = planned_minutes
planned_minutes = 60
print (planned_minutes)
print (actual_minutes)
When actual_minutes = planned_minutes runs, Python looks up planned_minutes and binds actual_minutes to the resulting value, 45. Reassigning planned_minutes later does not automatically rerun the copy.
For simple number and text examples, think of the trace as:
1
45
not assigned
2
45
45
3
60
45
Collections and mutable objects add an important sharing question. Unit 6 treats that topic carefully; do not generalize this simple trace to every Python object yet.
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 progress = document . createElement ("div" );
progress. className = "fcpython-quiz-progress" ;
const counter = document . createElement ("p" );
counter. className = "fcpython-quiz-counter" ;
counter. setAttribute ("aria-live" , "polite" );
progress. appendChild (counter);
const tabs = document . createElement ("div" );
tabs. className = "fcpython-quiz-steps" ;
tabs. setAttribute ("role" , "tablist" );
tabs. setAttribute ("aria-label" , "Quiz questions" );
progress. appendChild (tabs);
container. appendChild (progress);
const questions = document . createElement ("div" );
questions. className = "fcpython-quiz-questions" ;
const feedbackNodes = [];
const questionPanels = [];
const stepButtons = [];
let currentQuestion = 0 ;
function setStepStatus (questionIndex, status) {
const step = stepButtons[questionIndex];
const question = quiz. questions [questionIndex];
const statuses = {
answered : "answered" ,
correct : "correct" ,
incorrect : "incorrect" ,
unanswered : "not answered" ,
};
step. classList . remove (
"is-answered" ,
"is-correct" ,
"is-incorrect" ,
"is-unanswered"
);
if (status) {
step. classList . add (`is- ${ status} ` );
}
const statusLabel = status ? `, ${ statuses[status]} ` : "" ;
step. setAttribute (
"aria-label" ,
`Question ${ questionIndex + 1 } : ${ question. prompt }${ statusLabel} `
);
}
function showQuestion (questionIndex, focusPanel = false ) {
currentQuestion = Math . max (
0 ,
Math . min (questionIndex, quiz. questions . length - 1 )
);
questionPanels. forEach ((panel, index) => {
panel. hidden = index !== currentQuestion;
});
stepButtons. forEach ((step, index) => {
const isCurrent = index === currentQuestion;
step. classList . toggle ("is-current" , isCurrent);
step. setAttribute ("aria-selected" , String (isCurrent));
step. tabIndex = isCurrent ? 0 : - 1 ;
});
counter. textContent = `Question ${ currentQuestion + 1 } of ${ quiz. questions . length } ` ;
const selected = questionPanels[currentQuestion]. querySelector (
"input[type='radio']:checked"
);
const isLastQuestion = currentQuestion === quiz. questions . length - 1 ;
previous. hidden = currentQuestion === 0 ;
next. hidden = isLastQuestion || ! selected;
check. hidden = ! isLastQuestion;
if (focusPanel) {
questionPanels[currentQuestion]. focus ();
}
}
quiz. questions . forEach ((question, questionIndex) => {
const tabId = ` ${ quiz. id } -question-tab- ${ questionIndex + 1 } ` ;
const panelId = ` ${ quiz. id } -question-panel- ${ questionIndex + 1 } ` ;
const step = document . createElement ("button" );
step. type = "button" ;
step. className = "fcpython-quiz-step" ;
step. id = tabId;
step. textContent = String (questionIndex + 1 );
step. setAttribute ("role" , "tab" );
step. setAttribute ("aria-controls" , panelId);
step. setAttribute ("aria-selected" , "false" );
step. tabIndex = - 1 ;
step. addEventListener ("click" , () => showQuestion (questionIndex));
step. addEventListener ("keydown" , (event ) => {
let destination = null ;
if (event . key === "ArrowRight" ) {
destination = (questionIndex + 1 ) % quiz. questions . length ;
} else if (event . key === "ArrowLeft" ) {
destination =
(questionIndex - 1 + quiz. questions . length ) % quiz. questions . length ;
} else if (event . key === "Home" ) {
destination = 0 ;
} else if (event . key === "End" ) {
destination = quiz. questions . length - 1 ;
}
if (destination !== null ) {
event . preventDefault ();
showQuestion (destination);
stepButtons[destination]. focus ();
}
});
stepButtons. push (step);
tabs. appendChild (step);
setStepStatus (questionIndex, "" );
const panel = document . createElement ("div" );
panel. className = "fcpython-quiz-panel" ;
panel. id = panelId;
panel. setAttribute ("role" , "tabpanel" );
panel. setAttribute ("aria-labelledby" , tabId);
panel. tabIndex = - 1 ;
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = 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);
input. addEventListener ("change" , () => {
setStepStatus (questionIndex, "answered" );
feedbackNodes[questionIndex]. textContent = "" ;
feedbackNodes[questionIndex]. className = "fcpython-quiz-feedback" ;
score. textContent = "" ;
showQuestion (questionIndex);
});
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);
panel. appendChild (fieldset);
questionPanels. push (panel);
questions. appendChild (panel);
});
container. appendChild (questions);
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const previous = document . createElement ("button" );
previous. type = "button" ;
previous. className = "fcpython-quiz-secondary" ;
previous. textContent = "Previous" ;
previous. addEventListener ("click" , () => {
showQuestion (currentQuestion - 1 , true );
});
const next = document . createElement ("button" );
next. type = "button" ;
next. textContent = "Next question" ;
next. addEventListener ("click" , () => {
showQuestion (currentQuestion + 1 , true );
});
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. className = "fcpython-quiz-secondary fcpython-quiz-reset" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
let firstQuestionToReview = null ;
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" ;
setStepStatus (questionIndex, "unanswered" );
if (firstQuestionToReview === null ) {
firstQuestionToReview = questionIndex;
}
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" ;
setStepStatus (questionIndex, "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" ;
setStepStatus (questionIndex, "incorrect" );
if (firstQuestionToReview === null ) {
firstQuestionToReview = questionIndex;
}
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
if (firstQuestionToReview !== null ) {
showQuestion (firstQuestionToReview, true );
}
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
stepButtons. forEach ((_, questionIndex) => {
setStepStatus (questionIndex, "" );
});
score. textContent = "" ;
showQuestion (0 , true );
});
actions. appendChild (previous);
actions. appendChild (next);
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
showQuestion (0 );
return container;
}
7. Choose names that make change safer
These names are legal but unhelpful:
x = 45
y = 10
z = x - y
print (z)
This version exposes the program’s meaning:
session_minutes = 45
break_minutes = 10
focused_minutes = session_minutes - break_minutes
print (focused_minutes)
A useful beginner naming checklist:
use lowercase words separated by underscores: focused_minutes;
describe the value’s role, not only its type: course_name, not text;
include units when confusion is possible: duration_seconds;
use the same spelling everywhere;
avoid one-letter names except in a tiny mathematical example;
avoid names that Python already uses for common tools, such as print.
Lesson 3 gives the complete rules for valid identifiers and reserved keywords.
Legal does not always mean wise
This code runs:
But the name print now refers to 45 in the current notebook state. A later call such as print("hello") fails because Python finds the number instead of the built-in display tool.
If you do this accidentally in a notebook, restart the runtime or remove the binding before continuing. The safest habit is not to reuse familiar built-in names.
Rename without changing behavior
Improve this program:
a = 7
b = 6
c = a * b
print (c)
Choose one interpretation, such as boxes and items per box. Rename all four uses consistently. The output should remain 42.
Show one possible revision
box_count = 7
items_per_box = 6
item_total = box_count * items_per_box
print (item_total)
The new names expose the meaning of the multiplication. Different names are fine if they tell a coherent story and every use is updated.
8. Build a study-session summary
Create a new cell with this starter code:
course_name = "Python Foundations"
planned_minutes = 50
break_minutes = 10
focused_minutes = planned_minutes - break_minutes
print ("Course:" )
print (course_name)
print ("Focused minutes:" )
print (focused_minutes)
Before running, write the four displayed lines in order.
Then complete these changes one at a time:
Change planned_minutes to 65; predict the new focused time.
Add review_minutes = 15.
Add total_learning_minutes = focused_minutes + review_minutes.
Display a label and the new total.
Change break_minutes after the totals are calculated. Observe which stored values become stale.
Move the calculations to the correct place so every result reflects the latest inputs.
Rename one value consistently without changing the output.
Use a trace table to verify the final version
Your final table should have one row after each assignment:
1
value
—
—
—
—
2
value
value
—
—
—
…
…
…
…
…
…
Do not fill a cell from memory. Read the program in execution order and update only the binding changed by that line.
Hint: a reliable calculation order
Assign the input facts first. Calculate focused_minutes only after the final values of planned_minutes and break_minutes are available. Calculate total_learning_minutes only after both of its inputs are available. Display the results last.
Show one complete version
course_name = "Python Foundations"
planned_minutes = 65
break_minutes = 10
review_minutes = 15
focused_minutes = planned_minutes - break_minutes
total_learning_minutes = focused_minutes + review_minutes
print ("Course:" )
print (course_name)
print ("Focused minutes:" )
print (focused_minutes)
print ("Total learning minutes:" )
print (total_learning_minutes)
The output is:
Course:
Python Foundations
Focused minutes:
55
Total learning minutes:
70
9. Diagnose three state mistakes
For each example:
predict the output or error;
identify the first line that does not match the intended story;
make one repair;
rerun from the top.
The calculation happens too early
adult_tickets = 2
ticket_total = adult_tickets * 12
adult_tickets = 3
print (ticket_total)
If the intended total is for three tickets, move or repeat the calculation after the final assignment to adult_tickets.
The same fact has two spellings
workshop_minutes = 90
break_minute = 15
active_minutes = workshop_minutes - break_minutes
print (active_minutes)
Choose either singular or plural and use it consistently.
10. Check your model of assignment
{
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 progress = document . createElement ("div" );
progress. className = "fcpython-quiz-progress" ;
const counter = document . createElement ("p" );
counter. className = "fcpython-quiz-counter" ;
counter. setAttribute ("aria-live" , "polite" );
progress. appendChild (counter);
const tabs = document . createElement ("div" );
tabs. className = "fcpython-quiz-steps" ;
tabs. setAttribute ("role" , "tablist" );
tabs. setAttribute ("aria-label" , "Quiz questions" );
progress. appendChild (tabs);
container. appendChild (progress);
const questions = document . createElement ("div" );
questions. className = "fcpython-quiz-questions" ;
const feedbackNodes = [];
const questionPanels = [];
const stepButtons = [];
let currentQuestion = 0 ;
function setStepStatus (questionIndex, status) {
const step = stepButtons[questionIndex];
const question = quiz. questions [questionIndex];
const statuses = {
answered : "answered" ,
correct : "correct" ,
incorrect : "incorrect" ,
unanswered : "not answered" ,
};
step. classList . remove (
"is-answered" ,
"is-correct" ,
"is-incorrect" ,
"is-unanswered"
);
if (status) {
step. classList . add (`is- ${ status} ` );
}
const statusLabel = status ? `, ${ statuses[status]} ` : "" ;
step. setAttribute (
"aria-label" ,
`Question ${ questionIndex + 1 } : ${ question. prompt }${ statusLabel} `
);
}
function showQuestion (questionIndex, focusPanel = false ) {
currentQuestion = Math . max (
0 ,
Math . min (questionIndex, quiz. questions . length - 1 )
);
questionPanels. forEach ((panel, index) => {
panel. hidden = index !== currentQuestion;
});
stepButtons. forEach ((step, index) => {
const isCurrent = index === currentQuestion;
step. classList . toggle ("is-current" , isCurrent);
step. setAttribute ("aria-selected" , String (isCurrent));
step. tabIndex = isCurrent ? 0 : - 1 ;
});
counter. textContent = `Question ${ currentQuestion + 1 } of ${ quiz. questions . length } ` ;
const selected = questionPanels[currentQuestion]. querySelector (
"input[type='radio']:checked"
);
const isLastQuestion = currentQuestion === quiz. questions . length - 1 ;
previous. hidden = currentQuestion === 0 ;
next. hidden = isLastQuestion || ! selected;
check. hidden = ! isLastQuestion;
if (focusPanel) {
questionPanels[currentQuestion]. focus ();
}
}
quiz. questions . forEach ((question, questionIndex) => {
const tabId = ` ${ quiz. id } -question-tab- ${ questionIndex + 1 } ` ;
const panelId = ` ${ quiz. id } -question-panel- ${ questionIndex + 1 } ` ;
const step = document . createElement ("button" );
step. type = "button" ;
step. className = "fcpython-quiz-step" ;
step. id = tabId;
step. textContent = String (questionIndex + 1 );
step. setAttribute ("role" , "tab" );
step. setAttribute ("aria-controls" , panelId);
step. setAttribute ("aria-selected" , "false" );
step. tabIndex = - 1 ;
step. addEventListener ("click" , () => showQuestion (questionIndex));
step. addEventListener ("keydown" , (event ) => {
let destination = null ;
if (event . key === "ArrowRight" ) {
destination = (questionIndex + 1 ) % quiz. questions . length ;
} else if (event . key === "ArrowLeft" ) {
destination =
(questionIndex - 1 + quiz. questions . length ) % quiz. questions . length ;
} else if (event . key === "Home" ) {
destination = 0 ;
} else if (event . key === "End" ) {
destination = quiz. questions . length - 1 ;
}
if (destination !== null ) {
event . preventDefault ();
showQuestion (destination);
stepButtons[destination]. focus ();
}
});
stepButtons. push (step);
tabs. appendChild (step);
setStepStatus (questionIndex, "" );
const panel = document . createElement ("div" );
panel. className = "fcpython-quiz-panel" ;
panel. id = panelId;
panel. setAttribute ("role" , "tabpanel" );
panel. setAttribute ("aria-labelledby" , tabId);
panel. tabIndex = - 1 ;
const fieldset = document . createElement ("fieldset" );
fieldset. className = "fcpython-quiz-question" ;
const legend = document . createElement ("legend" );
legend. textContent = 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);
input. addEventListener ("change" , () => {
setStepStatus (questionIndex, "answered" );
feedbackNodes[questionIndex]. textContent = "" ;
feedbackNodes[questionIndex]. className = "fcpython-quiz-feedback" ;
score. textContent = "" ;
showQuestion (questionIndex);
});
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);
panel. appendChild (fieldset);
questionPanels. push (panel);
questions. appendChild (panel);
});
container. appendChild (questions);
const actions = document . createElement ("div" );
actions. className = "fcpython-quiz-actions" ;
const previous = document . createElement ("button" );
previous. type = "button" ;
previous. className = "fcpython-quiz-secondary" ;
previous. textContent = "Previous" ;
previous. addEventListener ("click" , () => {
showQuestion (currentQuestion - 1 , true );
});
const next = document . createElement ("button" );
next. type = "button" ;
next. textContent = "Next question" ;
next. addEventListener ("click" , () => {
showQuestion (currentQuestion + 1 , true );
});
const check = document . createElement ("button" );
check. type = "button" ;
check. textContent = "Check answers" ;
const reset = document . createElement ("button" );
reset. type = "button" ;
reset. className = "fcpython-quiz-secondary fcpython-quiz-reset" ;
reset. textContent = "Reset" ;
const score = document . createElement ("p" );
score. className = "fcpython-quiz-score" ;
score. setAttribute ("aria-live" , "polite" );
check. addEventListener ("click" , () => {
let correctCount = 0 ;
let firstQuestionToReview = null ;
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" ;
setStepStatus (questionIndex, "unanswered" );
if (firstQuestionToReview === null ) {
firstQuestionToReview = questionIndex;
}
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" ;
setStepStatus (questionIndex, "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" ;
setStepStatus (questionIndex, "incorrect" );
if (firstQuestionToReview === null ) {
firstQuestionToReview = questionIndex;
}
}
});
score. textContent = `Score: ${ correctCount} / ${ quiz. questions . length } ` ;
if (firstQuestionToReview !== null ) {
showQuestion (firstQuestionToReview, true );
}
});
reset. addEventListener ("click" , () => {
container. querySelectorAll ("input[type='radio']" ). forEach ((input) => {
input. checked = false ;
});
feedbackNodes. forEach ((feedback) => {
feedback. textContent = "" ;
feedback. className = "fcpython-quiz-feedback" ;
});
stepButtons. forEach ((_, questionIndex) => {
setStepStatus (questionIndex, "" );
});
score. textContent = "" ;
showQuestion (0 , true );
});
actions. appendChild (previous);
actions. appendChild (next);
actions. appendChild (check);
actions. appendChild (reset);
container. appendChild (actions);
container. appendChild (score);
showQuestion (0 );
return container;
}
Key points
A literal writes a value directly in source code.
Assignment evaluates the right side, then binds its result to the name on the left.
Looking up a name returns the value currently bound to it.
Reassignment changes a binding for future lookups; it does not rewrite earlier source code.
Calculated results do not refresh automatically when an input changes.
Trace tables make state and execution order visible.
Good names preserve meaning and make later changes safer.
Detailed types, conversion, and user input belong to Unit 2.
Back to top