Learn · Run · Break · Fix
A learning series from A Wandering Mind
Eighteen interlinked lessons on strings, lists, files, exceptions, and dictionaries—with original explanations, runnable examples, real output, and supporting CPSC 1301K lectures.
>>> guide = { ... "lessons": 18, ... "modules": 5, ... "python": "3.10+" ... } >>> guide["goal"] = "understand it"
Looping Over Lists and Nested Lists in Python
A list becomes much more powerful when your program can process every item without writing a separate line for each one. Python’s for loop walks through list elements in order, and nested lists let one list contain other lists. In this final lesson of Module 2, you will combine those ideas to process one-dimensional and two-dimensional collections cleanly.
A for loop visits each list item in order
The most direct way to process every element in a list is to loop over the list itself. The pattern for item in items: asks Python to take each element from the list, one at a time, assign it temporarily to item, and run the indented block.
This is usually clearer than manually indexing items[0], items[1], items[2], and so on. It also keeps working if the list later contains more or fewer elements.
fruits = ["apple", "banana", "pear"]
for fruit in fruits:
print(fruit)
apple
banana
pear
On each pass, fruit refers to the next string in the list. The loop ends automatically after the final element. You do not need to calculate the list length or update an index variable yourself.
Prefer the value when the value is what you need. If your code only needs each element, for item in items is simpler than looping over a range of indexes and then looking up each item.
Use enumerate() when you need a position too
Sometimes the item is not enough. A numbered menu, ranked result, or progress display may need both the value and a counter. Python’s built-in enumerate() pairs each item with a position-like number while you loop.
By default, enumerate() starts counting at zero. Passing start=1 is useful when the number is meant for people rather than as a true list index.
tasks = ["email", "meeting", "backup"]
for position, task in enumerate(tasks, start=1):
print(f"{position}. {task}")
1. email
2. meeting
3. backup
The loop receives two values on each iteration: the generated number and the actual list element. This is different from manually calling len() or indexing by position; enumerate() is designed specifically for “number plus item” iteration.
The loop variable is a temporary name
During a direct loop, the variable after for is reassigned to the next element on each pass. That variable is a convenient name for the current value; it is not a permanent extra slot inside the list. After the loop finishes, the list still contains its original sequence unless your loop explicitly performs a list mutation such as index assignment, append(), or another modifying operation.
This distinction helps when tracing code: first ask what value the loop variable refers to on the current iteration, then ask whether the body merely reads that value or actually changes the collection.
A nested list stores lists inside a list
A list element can itself be another list. This creates a nested structure that can represent rows in a table, a grid, grouped measurements, game boards, matrices, or other data that naturally has more than one level.
The outer list contains the rows. Each row is another list containing its own elements. Accessing one row therefore uses one index; accessing one value inside a row uses two indexes.
grid = [
["A1", "A2", "A3"],
["B1", "B2", "B3"],
]
print(grid[0])
print(grid[1][2])
['A1', 'A2', 'A3']
B3
grid[0] returns the entire first inner list. grid[1][2] first selects the second row, then selects index 2 inside that row. Read it one level at a time: outer position first, inner position second.
Nested loops process nested lists naturally
If one for loop can visit every row, another loop inside it can visit every value in that row. This is a nested loop: the inner loop completes all of its iterations for the current outer item before the outer loop moves to the next row.
temperatures = [
[72, 75, 78],
[68, 71, 73],
]
for row in temperatures:
for value in row:
print(value, end=" ")
print()
72 75 78
68 71 73
The outer loop receives one row list at a time. The inner loop then visits every number in that row. The final print() runs after the inner loop, creating a new output line before the next row begins.
for item in items:Best when you need each element itself.
for i, item in enumerate(items):Gives a counter and the element together.
grid[row][column]Selects an outer list first, then an element inside it.
for row in grid: ...Use an inner loop to process values within each row.
Common mistakes with loops and nested lists
The tracebacks below come from executed Python code. They show four different ways the structure of the data can fail to match what the loop or index expression expects.
count = 3
for value in count:
print(value)
Traceback (most recent call last):
File "/tmp/ipykernel_313/676502045.py", line 14, in run_err
exec(compile(code, filename, "exec"), {})
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/awm_lesson10/mistake1_iterate_int.py", line 2, in <module>
for value in count:
^^^^^
TypeError: 'int' object is not iterable
A for loop needs something iterable—such as a list, string, range, or similar object. A plain integer is one numeric value, not a sequence of elements for the loop to visit.
items = ["a", "b", "c"]
for index, value in items:
print(index, value)
Traceback (most recent call last):
File "/tmp/ipykernel_313/676502045.py", line 14, in run_err
exec(compile(code, filename, "exec"), {})
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/awm_lesson10/mistake2_bad_unpack.py", line 2, in <module>
for index, value in items:
^^^^^^^^^^^^
ValueError: not enough values to unpack (expected 2, got 1)
The loop expects each list element to provide two values, but each element here is a one-character string. If you want a generated counter plus the item, use enumerate(items) instead of trying to unpack each element.
grid = [["A1", "A2"], ["B1", "B2"]]
print(grid[1][2])
Traceback (most recent call last):
File "/tmp/ipykernel_313/676502045.py", line 14, in run_err
exec(compile(code, filename, "exec"), {})
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/awm_lesson10/mistake3_nested_index_too_far.py", line 2, in <module>
print(grid[1][2])
~~~~~~~^^^
IndexError: list index out of range
The outer index 1 is valid, but the selected inner list only has indexes 0 and 1. Each level of a nested list has its own valid index range.
values = [10, 20, 30]
print(values[0][1])
Traceback (most recent call last):
File "/tmp/ipykernel_313/676502045.py", line 14, in run_err
exec(compile(code, filename, "exec"), {})
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/awm_lesson10/mistake4_second_index_on_int.py", line 2, in <module>
print(values[0][1])
~~~~~~~~~^^^
TypeError: 'int' object is not subscriptable
values[0] returns the integer 10. The second [1] then tries to index that integer as though it were another sequence. Two indexes only make sense when the first lookup actually returns something indexable.
Supporting classroom explanations
This page is original A Wandering Mind instructional material. The two CPSC 1301K lectures below reinforce looping directly over list values and working with multidimensional list structures.
What it adds: a classroom walkthrough of using for loops to process list elements without manually managing every index.
What it adds: a visual explanation of lists containing other lists and how multiple index levels represent positions within that structure.
Try it yourself
For each exercise, predict the order in which values will be visited. With nested lists, identify the outer list and inner list separately before tracing the loop.
Exercise 1 · Print every language
Create languages = ["Python", "Java", "SQL"]. Use a for loop to print each language on its own line.
Show one answer
languages = ["Python", "Java", "SQL"]
for language in languages:
print(language)
Python
Java
SQL
Exercise 2 · Number three stops
Create stops = ["Savannah", "Macon", "Atlanta"]. Use enumerate(..., start=1) to print a numbered list.
Show one answer
stops = ["Savannah", "Macon", "Atlanta"]
for number, stop in enumerate(stops, start=1):
print(f"{number}. {stop}")
1. Savannah
2. Macon
3. Atlanta
Exercise 3 · Walk a two-row grid
Create grid = [[1, 2], [3, 4]]. Use nested loops to print each value on its own line in row order.
Show one answer
grid = [[1, 2], [3, 4]]
for row in grid:
for value in row:
print(value)
1
2
3
4
Module 2 complete: what comes next
You have now worked through list creation, indexing, mutation, concatenation, repetition, slicing, membership, references, copying, methods, built-in functions, loops, and nested structures. That is enough list knowledge to begin using collections as practical program data rather than isolated examples.
Module 3 moves that data outside the running program. Continue to How File I/O Works, and Opening a File to learn how Python connects your code to files stored on disk.
