A Wandering Mind · Computer Science
PYTHON GUIDE

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.

course.py
>>> guide = {
...   "lessons": 18,
...   "modules": 5,
...   "python": "3.10+"
... }
>>> guide["goal"] = "understand it"
Module 2 · Lists · Lesson 10 of 18

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.

Prerequisite: Complete List Methods and Built-in Functions first. You should already be comfortable creating, indexing, modifying, slicing, and inspecting lists.

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.

Example 1 · Loop directly over a listVerified
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.

Example 2 · Number items with enumerate()Verified
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.

Example 3 · Index into a nested listVerified
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.

Example 4 · Loop through rows and valuesVerified
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.

Loop over valuesfor item in items:

Best when you need each element itself.

Numbered loopfor i, item in enumerate(items):

Gives a counter and the element together.

Nested accessgrid[row][column]

Selects an outer list first, then an element inside it.

Nested loopsfor 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.

Mistake 1 · Trying to loop over an integer
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.

Mistake 2 · Unpacking a string element into two variables
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.

Mistake 3 · Going beyond the inner list
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.

Mistake 4 · Treating an integer element like another list
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.

Lists and For Loops — by Hyrum Carroll · 7:27. Watch on YouTube ↗ · CPSC 1301K playlist ↗

What it adds: a classroom walkthrough of using for loops to process list elements without manually managing every index.

Multidimensional Lists — by Hyrum Carroll · 5:35. Watch on YouTube ↗ · CPSC 1301K playlist ↗

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.