Skip to content
VLSI Mentor

Python Part I · Module 5 · Lists

Going Through a List

You can build a list, reach into it and change it. What you cannot yet do is the thing lists exist for: perform the same work on every element without naming each one by hand. Three tests is tedious; three hundred is impossible. This chapter introduces the one piece of syntax that solves it — a for loop over a list — and deliberately introduces nothing else. The question here is narrow and practical: how do I visit each element of the list I have just learned? A later module takes loops apart as a general capability, with counting, waiting, stopping early and all the rest. For now, one shape, understood properly.

Foundation14 min readPythonPart IFundamentalsListsIteration

Part I · Module 5 · Chapter 5.4 · Going Through a List

1. The Problem a List Eventually Creates

Everything in this module so far has been about holding and shaping data. You can write down a list of tests, reach any element, sort it, add to it. But look at what it still takes to actually do something to every element:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt"]
print(tests[0])
print(tests[1])
print(tests[2])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
smoke
reset
interrupt

That works, and it is wrong in two ways that matter.

It does not survive a change. Add a fourth test and the code silently prints only three — no error, just a missing line. Remove one and it raises IndexError.

And it does not scale. This is three lines for three tests. A real test list has hundreds, and a log file has thousands of lines.

What you want is to say "do this to every element" once, without knowing or caring how many there are. That is what this chapter is for.

2. The Smallest Loop

Here is the whole idea in three lines:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt"]
for test in tests:
    print(test)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
smoke
reset
interrupt

Three elements in the list, three lines of output, and the print was written once.

This is new syntax, so take it one piece at a time:

  • for — a keyword that starts the loop. It tells Python that what follows is work to be repeated.
  • test — a name, which you choose. On each pass it will refer to one element of the list.
  • in — a keyword separating the name from the thing being gone through. This is part of the for syntax; it is not the membership operator from Chapter 3.6, even though it is spelled the same.
  • tests — the list to go through.
  • : — a colon ending the line, which says "the block that follows belongs to this."
  • the indented line — the body of the loop: the work done once per element.

Read aloud, for test in tests: is "for each element of tests, call it test, and then do this."

3. The Loop Variable Is a Name

This is the part to get right, and it needs nothing new — only Chapter 2.3's model.

test is an ordinary name, and it refers to a value. What the loop does on each pass is re-point that name at the next element:

Passtest refers toOutput
1"smoke"smoke
2"reset"reset
3"interrupt"interrupt
—list exhaustedloop ends

The body — print(test) — ran once per pass, three times in all, with test meaning something different each time.

Nothing is being copied into a container, and test is not a special kind of variable. It is the same thing tests = [...] made on line 1 — a name referring to a value — and the loop simply assigns to it repeatedly.

One consequence worth knowing, because it surprises people: the name still exists after the loop finishes, referring to whatever it was pointed at last.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
last = ["a", "b", "c"]
for item in last:
    print(item)
print(item)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a
b
c
c

The fourth line is c — the loop variable survived, holding the final element. That is not usually something to rely on, but it explains the result if you ever print a loop variable afterwards and wonder why it has a value.

4. Order Comes From the List

Chapter 5.1 established that order is part of a list's value. Iteration simply follows it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["interrupt", "smoke", "reset"]
for test in tests:
    print(test)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interrupt
smoke
reset

Same three names as Section 2, written in a different order, visited in that different order. The loop has no opinion about ordering — it walks the list from the first element to the last. If you want a particular order, put the list in that order first, which is what Chapter 5.5 was for.

Duplicates are visited separately, for the same reason they were counted separately:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
statuses = ["PASS", "FAIL", "PASS"]
for status in statuses:
    print(status)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
PASS
FAIL
PASS

Three elements, three passes. The loop does not notice that two of them are equal.

5. Zero, One and Many

The number of passes is simply the number of elements. That sounds obvious and has one genuinely useful consequence.

One element, one pass:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke"]
for test in tests:
    print(test)
print("done")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
smoke
done

No elements, no passes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failures = []
for failure in failures:
    print(failure)
print("done")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
done

The body never ran. Not an error, not a warning — there were no elements, so there was no work, and the program continued to the next line.

That is worth pausing on, because it is the single most useful property of a loop for real scripts:

Elements in the listTimes the body runs
00
11
33
NN

6. The Body Is Everything Indented

Chapter 1.10 established that indentation marks a block in Python. The loop body is exactly that: every line indented under the for line.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset"]
for test in tests:
    print("running")
    print(test)
print("finished")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
running
smoke
running
reset
finished

Read the output carefully. running appears twice, because both indented lines are part of the body and the whole body repeats once per element. finished appears once, at the end, because it is not indented — it is not part of the loop, so it runs after the loop has finished.

That is the whole rule: indented under the for line means repeated; back at the original margin means after.

7. Broken on Purpose

Four failures, all executed. These are the ones you will actually cause.

Forgetting to indent the body. Python needs a block and there is not one:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
tests = ["smoke", "reset"]
for test in tests:
print(test)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  File "bad.py", line 3
    print(test)
    ^^^^^
IndentationError: expected an indented block after 'for' statement on line 2

The message is unusually specific and worth reading in full: it names what it expected (an indented block), what it was for (a for statement) and where that statement was (line 2).

Forgetting the colon:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
tests = ["smoke", "reset"]
for test in tests
    print(test)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  File "bad.py", line 2
    for test in tests
                     ^
SyntaxError: expected ':'

Python read the whole for line, reached the end, and found no colon. The caret points past the last character — at the place the colon should have been.

Indenting inconsistently inside the body:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
tests = ["smoke", "reset"]
for test in tests:
    print("running")
        print(test)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  File "bad.py", line 4
    print(test)
IndentationError: unexpected indent

The first body line set the indentation; the second went deeper, and Python has no meaning for that. Every line of one block sits at the same depth.

Looping over something that has no elements to give:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
for x in 4210:
    print(x)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
TypeError: 'int' object is not iterable

That word iterable is the one Chapter 5.9 introduced: a value that can supply its contents one at a time. A list is iterable; a single number is not, because it has no contents to hand out. A for loop needs something iterable on the right of in.

8. Doing Something With Each Element

Printing is the smallest possible body. The loop becomes useful when the body does real work, and every operation you already know is available because test is an ordinary value.

String methods from Module 4 work directly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt"]
for test in tests:
    print(test.upper())
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
SMOKE
RESET
INTERRUPT

And here is a point worth checking rather than assuming — the list itself is untouched:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt"]
for test in tests:
    print(test.upper())
print(tests)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
SMOKE
RESET
INTERRUPT
['smoke', 'reset', 'interrupt']

Still lowercase. That is Chapter 4.11 doing exactly what it promised: upper() returns a new string and changes nothing. The loop read each element; it did not modify any of them.

len() works on each element, because each element is a string:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
signals = ["clk", "reset_n", "valid"]
for signal in signals:
    print(signal)
    print(len(signal))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk
3
reset_n
7
valid
5

Tests from Chapter 4.10:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
paths = ["rtl/top.sv", "tb/top_tb.sv"]
for path in paths:
    print(path.endswith(".sv"))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
True

Arithmetic, when the elements are numbers:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
runtimes = [12.4, 8.7, 19.2]
for runtime in runtimes:
    print(runtime * 2)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
24.8
17.4
38.4

Nothing in any of those is new except the two loop lines around them. That is the point of this chapter: the loop does not change what you can do to a value, it changes how many values you can do it to.

Building a second list

Combining the loop with append from Chapter 5.3 gives you the shape most real scripts have — go through one list, build another:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
src = ["smoke", "reset"]
upper = []
for test in src:
    upper.append(test.upper())
print(upper)
print(src)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['SMOKE', 'RESET']
['smoke', 'reset']

Start with an empty list, add one element per pass, and the original is untouched. Chapter 5.3 called this the accumulation pattern and said the part that decides when to append would come later; this is the part that supplies the repetition.

9. The Loop Variable Is the Element, Not Its Position

This single confusion breaks more beginner loops than anything else in the chapter, so it is worth being blunt about.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
signals = ["clk", "reset_n"]
for signal in signals:
    print(signal, type(signal))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk <class 'str'>
reset_n <class 'str'>

signal refers to the element — the string "clk", then the string "reset_n". It does not refer to 0 and then 1.

So there is no indexing to do. print(signal) is correct; print(signals[signal]) would be nonsense, because signal is a string and a list position must be a whole number — exactly the TypeError Chapter 5.2 showed.

10. Predict, Run, Observe

Predict all of it before reading on: how many times the body runs, what test refers to on each pass, the order of the output, and where done appears.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset"]
for test in tests:
    print(test.upper())
print("done")

The verified output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
SMOKE
RESET
done

Two elements, so the body ran twice. test referred to "smoke" then "reset". The output follows list order. And done appears last and once, because that line is not indented — Section 6.

Now a second one, where the body has two lines:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
runtimes = [12.4, 8.7]
for runtime in runtimes:
    print(runtime)
    print(runtime + 1)
print("end")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
12.4
13.4
8.7
9.7
end

Note the interleaving. Both body lines run for the first element before either runs for the second — the whole body completes each pass. If you predicted 12.4 / 8.7 / 13.4 / 9.7 you read the loop as running each line all the way down the list, which is not what happens.

11. Do Not Add or Remove While Looping

One warning, because Module 5 taught you how to change a list and this is where that gets you into trouble.

Reading the list during a loop is completely fine:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
items = ["a", "b", "c"]
for item in items:
    print(item, len(items))
print(items)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a 3
b 3
c 3
['a', 'b', 'c']

No problem at all. The body looked at the list on every pass and nothing surprising happened.

Changing the list's structure while looping over it is a different matter:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
items = ["a", "b", "c"]
for item in items:
    print(item)
    items.remove(item)
print(items)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a
c
['b']

Look at what happened. The loop printed a and c — b was never visited — and the list still has an element left in it. Nothing raised, nothing warned.

The reason is that the loop is walking the list by position while you are changing which elements sit at which position. Removing "a" shifts "b" into the position the loop has already passed, so the next pass goes to what is now position 1 — "c" — and "b" is skipped entirely.

That is deliberately a rule rather than an explanation of every case. There are correct ways to remove items while processing a collection, and they use tools from later modules. Until then, the second-list approach handles everything you will need and cannot go wrong.

12. What Is Still Missing

You can now perform the same operation on every element of a list. Two things are still out of reach, and both are deliberate.

You cannot yet do something different for some elements — print only the failures, skip the comment lines. Choosing is if, and Module 9 covers it.

And this chapter has shown you exactly one loop shape: going through a list, one element at a time, from first to last. Module 10 — Loops takes loops apart as a capability in their own right: counting without a list, repeating while you wait for something, stopping early, walking two collections together, and what the same loop does when given a collection that is not a list.

Everything you have learned here stays true when those arrive. A list is the thing most loops go through, which is why the loop had to be introduced here at all.

13. Exercises

Exercise 1 — the smallest loop. Write the three lines that print each of ["clk", "reset_n", "valid"] on its own line. Then predict, without running it, how many lines of output a list of seven signals would produce.

Exercise 2 — predict the order. Give the exact output of each, then verify:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for status in ["PASS", "FAIL", "PASS"]:
    print(status.lower())
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for runtime in [12.4, 8.7]:
    print(runtime + 1)

Exercise 3 — how many passes. For each list, say how many times the body runs: (a) ["a", "b", "c"] (b) ["a"] (c) [] (d) ["a", "a"] (e) a list of 250 paths

Exercise 4 — indented or not. Predict the exact output, then run it. Say which lines repeat and why:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset"]
for test in tests:
    print("start")
    print(test)
print("stop")

Then move print("stop") to be indented with the others and predict the new output before running it.

Exercise 5 — fix the indentation. This raises. Write down the exact error, say what Python expected, and fix it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
paths = ["rtl/top.sv"]
for path in paths:
print(path)

Exercise 6 — element, not position. A colleague writes this and gets a TypeError. Explain in two sentences what they assumed the loop variable was, what it actually is, and give the corrected body:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
signals = ["clk", "reset_n"]
for signal in signals:
    print(signals[signal])

Exercise 7 — use the element. Given paths = ["rtl/top.sv", "tb/top_tb.sv", "doc/readme.md"], write a loop that prints True or False for each depending on whether it ends in .sv. Predict the three lines of output before running it.

Exercise 8 — build a second list. Starting from names = ["smoke", "reset"], write a loop that builds a new list holding the length of each name, then print both lists. Say in one sentence why the original is unchanged.

Exercise 9 — the empty list. Explain in two sentences why a loop over [] prints nothing from its body and does not raise, and why that is useful in a script that reports failures.

Exercise 10 — the dangerous one. Predict the output and the final list, then run it and explain the discrepancy in three sentences:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
items = ["a", "b", "c"]
for item in items:
    print(item)
    items.remove(item)
print(items)

14. Summary

  • A for loop runs its body once for each element of a list, in list order. It is how a stored collection becomes work actually done.
  • for test in tests: reads as "for each element of tests, call it test, and do this."
  • The loop variable is an ordinary name. On each pass Python points it at the next element — the same name-and-value model as every other assignment.
  • It refers to the element, not to its position. signals[signal] is the classic mistake.
  • Order comes from the list, and duplicates are visited separately.
  • The number of passes is the number of elements: 0 elements means the body never runs, which is not an error and needs no special handling.
  • The body is everything indented under the for line. Unindented lines after it run once, when the loop has finished.
  • IndentationError: expected an indented block means you forgot to indent the body; SyntaxError: expected ':' means you forgot the colon.
  • TypeError: 'int' object is not iterable means the thing after in has no contents to hand out.
  • Every operation you already know works inside the body — string methods, len(), tests, arithmetic — because the loop variable is an ordinary value.
  • A loop that reads elements does not change the list. upper() still returns a new string.
  • Going through one list and appending to another is the shape most scripts have.
  • Do not add or remove elements from the list you are looping over. It silently skips items. Build a second list instead.

Module 5 is now complete. The next module takes the collection you know well and removes one property from it.

Where this fits

Part of the Python curriculum.