Questions
What problem does Lists 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 lists 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: Lists
A list is an ordered, mutable collection. You can read items by index, add or remove items, loop over them, slice them, and copy them when you need an independent list.
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 list after each line, not only at the end.
tasks = ["read" , "practice" ]
tasks.append("reflect" )
tasks[0 ] = "predict"
first_two = tasks[:2 ]
print (tasks)
print (first_two)
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
The list begins with two items at indexes 0 and 1.
append mutates the existing list by adding one new item at the end.
tasks[0] = ... replaces the first item but leaves the other positions alone.
tasks[:2] creates a new list containing indexes 0 and 1, stopping before index 2.
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
Assign alias = tasks and then run alias.append("review"). Both names show the change because they point to the same list. Then try copy = tasks.copy() and compare the behavior.
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: Assign alias = tasks and then run alias.append("review").
Debugging checkpoint 1.1
The error IndexError: list index out of range means the position you asked for does not exist. Check len(items) and remember that the last positive index is len(items) - 1. Also remember that scores.sort() changes the list and returns None; use sorted(scores) when you need a new sorted list.
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
Build a grade list in Colab. Add one grade, replace one mistaken grade, compute the total with a loop, sort a copy, and write one sentence explaining the difference between the original and the copy.
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
A single variable can store one value. Real programs often need a group of values: many scores, many names, many tasks, many measurements. A list stores multiple values in one ordered container.
Lists are one of the first data structures worth mastering because they combine several core ideas:
order: items have positions;
mutability: the list can change;
iteration: loops can visit each item;
indexing and slicing: code can pick one item or a smaller list.
How lists store ordered items
A list is one object that stores items in order. Names can point to that same list, or to a separate copy.
flowchart LR
tasks["name<br/>tasks"] --> list["list object<br/>index 0: 'predict'<br/>index 1: 'practice'<br/>index 2: 'reflect'"]
alias["name<br/>alias"] --> list
copy["name<br/>copy"] --> copylist["separate list object<br/>index 0: 'predict'<br/>index 1: 'practice'"]
list --> slice["slice tasks[1:3]<br/>new list with indexes 1 and 2"]
This diagram explains why aliasing can surprise beginners: two names can point to one mutable list object.
1. Creating, reading, and changing lists
A list is written with square brackets. Items are separated by commas.
scores = [10 , 8 , 9 ]
names = ["Ada" , "Grace" , "Katherine" ]
A list can hold any type of value, but beginner programs are usually easier to understand when one list contains one kind of thing.
Example 1
Predict the final printed list before running the code.
tasks = ["read" , "practice" ]
tasks.append("reflect" )
tasks[0 ] = "predict"
print (tasks)
Step-by-step explanation
tasks = ["read", "practice"]
Python creates a list with two strings.
Index 0 contains "read"; index 1 contains "practice".
tasks.append("reflect")
.append(...) adds a new item to the end of the existing list.
The list is now ["read", "practice", "reflect"].
tasks[0] = "predict"
Index 0 is replaced.
The list is now ["predict", "practice", "reflect"].
print(tasks)
Python displays the whole list.
Indexes and length
colors = ["red" , "green" , "blue" ]
print (colors[0 ]) # red
print (colors[2 ]) # blue
print (len (colors)) # 3
len(colors) is 3, but the last index is 2. This is a common beginner surprise: indexes start at 0, so a list of length 3 has indexes 0, 1, and 2.
Common mistakes
Mistake: reaching past the end.
colors = ["red" , "green" , "blue" ]
print (colors[3 ])
This raises IndexError. The last valid index is len(colors) - 1, which is 2 here.
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. List methods and loops
A method is an action attached to a value. List methods change or inspect a list.
.append(item)
add item to the end
tasks.append("review")
.remove(item)
remove first matching item
tasks.remove("read")
.pop()
remove and return the last item
last = tasks.pop()
.sort()
sort the list in place
scores.sort()
Example 2
scores = [70 , 90 , 80 ]
scores.append(100 )
scores.sort()
for score in scores:
print (score)
Step-by-step explanation
scores = [70, 90, 80] creates a list.
scores.append(100) changes that same list by adding 100 at the end.
scores.sort() changes the list order to [70, 80, 90, 100].
for score in scores: visits each score one at a time.
The indented print(score) runs once per score.
Method return values
Some list methods change the list and return None. This mistake is common:
scores = [70 , 90 , 80 ]
sorted_scores = scores.sort()
print (sorted_scores)
This prints None because .sort() changes scores in place. If you want a new sorted list, use sorted(...):
scores = [70 , 90 , 80 ]
sorted_scores = sorted (scores)
print (sorted_scores)
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. Slicing, copying, and aliasing
A slice creates a smaller list from part of another list.
letters = ["a" , "b" , "c" , "d" ]
print (letters[1 :3 ])
The result is ["b", "c"]. The start index 1 is included. The stop index 3 is not included.
Why stop is not included
Think of the slice as cutting between items:
indexes: 0 1 2 3
items: "a" "b" "c" "d"
cut: | | | | |
start stop
letters[1:3] starts at index 1 and stops before index 3.
Aliasing: two names, one list
original = ["read" , "practice" ]
alias = original
alias.append("reflect" )
print (original)
This prints ["read", "practice", "reflect"]. alias and original are two names for the same list.
If you need a separate list, copy it:
original = ["read" , "practice" ]
copy = original.copy()
copy.append("reflect" )
print (original)
print (copy)
Common mistakes
Mistake: expecting assignment to copy a list.
alias = original does not copy the list. It adds another name for the same list object. Use .copy() or a slice original[:] for a shallow copy.
Practice
In Colab, create a shopping list and practice one operation at a time:
shopping = ["rice" , "beans" , "apples" ]
shopping.append("tea" )
shopping.remove("beans" )
print (shopping[:2 ])
Before each line, predict the exact list.
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
Lists store ordered collections of values.
Indexes start at 0; the last index is len(items) - 1.
Lists are mutable: methods and item assignment can change them.
A for loop can process each list item.
Slices use start:stop, where stop is not included.
Assignment creates an alias, not a copy.
References
Python Tutorial: lists: https://docs.python.org/3/tutorial/introduction.html#lists
Python Tutorial: more on lists: https://docs.python.org/3/tutorial/datastructures.html#more-on-lists
Back to top