Skip to content
VLSI Mentor

Python Part I · Module 5 · Lists

Indexing and Slicing a List

This is the easiest chapter in the module, because you already did the hard part. Chapters 4.4 and 4.5 worked out how Python numbers positions, why counting starts at zero, what negative numbers mean, and why a slice excludes its stop boundary. All of that applies to lists unchanged — there is no second system to learn. What this chapter is really for is the three places where lists give you something different from what a string gave you, and the third of those is the one that quietly confuses people: an element of a list can itself be indexed, so a single expression can carry two different position systems at once.

Foundation12 min readPythonPart IFundamentalsListsIndexing

Part I · Module 5 · Chapter 5.2 · Indexing and Slicing a List

1. The Rules Are the Ones You Already Know

Positions in a list are numbered exactly as positions in a string were:

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

tests[0] is the first element. tests[-1] is the last. If either of those surprised you, the place to go is Chapter 4.4 — it is the same numbering, established there, and this chapter assumes it.

All four positions, both ways round:

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

Same four elements reached by two numbering systems, which is Chapter 4.4's ruler with elements in the cells instead of characters. And the last valid index is still one less than the length:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt", "stress"]
print(len(tests), tests[len(tests) - 1])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
4 stress

That is the whole of the transfer. The rest of this chapter is about what is different.

2. An Index Gives You the Element Itself

Here is the first real difference. Indexing a string gave you back a one-character string, because that is all a string is made of. Indexing a list gives you back the element, whatever kind of value it happens to be:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
values = ["smoke", 4210, True]
print(type(values[0]))
print(type(values[1]))
print(type(values[2]))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
<class 'str'>
<class 'int'>
<class 'bool'>

Three indexes into one list, three different types out. Nothing was converted and nothing was wrapped — you got the values that are in the list.

Put the two side by side and the difference is sharp:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
word = "smoke"
tests = ["smoke", "reset"]
print(word[0], type(word[0]), len(word[0]))
print(tests[0], type(tests[0]), len(tests[0]))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
s <class 'str'> 1
smoke <class 'str'> 5

word[0] is "s" — one character, length 1. tests[0] is "smoke" — the whole element, length 5. Both happen to be strings here, but for entirely different reasons: one is a character of a string, the other is an element that is a string.

3. A Slice Gives You a List

The slicing notation is unchanged — [start:stop], start included, stop excluded, boundaries between positions. Chapter 4.5 built that model and it carries over exactly.

What is different is the kind of value that comes back:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt", "stress"]
subset = tests[1:3]
print(subset)
print(type(subset))
print(len(subset))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['reset', 'interrupt']
<class 'list'>
2

A slice of a list is a list. That is consistent with strings — a slice of a string was a string — but it has a consequence that catches people, and it is worth meeting head-on:

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

These are not the same value. tests[0] is the string "smoke". tests[0:1] is a list containing one string. They print differently, they have different types, and len() gives 5 for one and 1 for the other.

4. The Slice Rules, Confirmed Not Re-Taught

Everything Chapter 4.5 established holds. Rather than re-derive it, here it is working:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt", "stress"]
print(tests[:2])
print(tests[2:])
print(tests[:])
print(tests[-2:])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['smoke', 'reset']
['interrupt', 'stress']
['smoke', 'reset', 'interrupt', 'stress']
['interrupt', 'stress']

Omit the start for "from the beginning", the stop for "to the end", both for everything. tests[-2:] is "the last two" — the idiom Chapter 4.5 recommended keeping, now on a list.

The length rule holds too:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt", "stress"]
print(len(tests[1:3]), 3 - 1)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 2

And adjacent slices still tile exactly, with no element lost or duplicated:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt", "stress"]
print(tests[:2] + tests[2:] == tests)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True

That + between two lists is joining them, which is Chapter 5.6's subject — here it is only being used to show the tiling.

An out-of-range slice still does not raise, exactly as it did not for strings:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt", "stress"]
print(tests[0:100])
print(tests[10:20])
print(tests[3:1])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['smoke', 'reset', 'interrupt', 'stress']
[]
[]

As much as exists, then an empty list rather than an empty string — the same forgiving behaviour with the type adjusted. And the same warning applies: an empty result where you expected elements usually means a boundary is wrong, and nothing will tell you.

5. Indexing an Element

Now the third difference, and the one worth slowing down for.

An element of a list is an ordinary value. If that element is a string, then everything from Module 4 applies to it — including indexing and slicing:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tests = ["smoke", "reset", "interrupt", "stress"]
print(tests[0][0])
print(tests[1][:3])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
s
res

Read tests[0][0] strictly left to right, one bracket at a time:

  • tests[0] is evaluated first. It gives the element at position 0, which is the string "smoke".
  • [0] is then applied to that string, giving its first character, "s".

So the two brackets are counting in two different things. The first counts elements of a list; the second counts characters of a string. They are not two dimensions of one structure — they are two separate operations, and the second one only works because the first one produced something indexable.

tests[1][:3] is the same idea with a slice on the end: element 1 is "reset", and [:3] takes its first three characters, "res".

6. Broken on Purpose

Four failures, all executed. The wording differs from the string versions, which is why they are worth seeing rather than assuming.

A position that does not exist:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
tests = ["smoke", "reset"]
print(tests[2])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
IndexError: list index out of range

list index out of range rather than string index out of range. Two elements means valid positions 0 and 1, and 2 is one past the end — the same off-by-one, and the same first thing to check: the index against len().

Too far negative, which is the same error from the other end:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
tests = ["smoke", "reset"]
print(tests[-3])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
IndexError: list index out of range

An empty list has no valid position at all, not even 0:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
print([][0])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
IndexError: list index out of range

This one matters in practice. A list of failures is legitimately empty when nothing failed, and failures[0] on it raises rather than giving you something blank.

A float is not a position:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
print(["a", "b"][1.0])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
TypeError: list indices must be integers or slices, not float

A TypeError rather than an IndexError — the position was not out of range, it was the wrong kind of value. That message is also more informative than the string one: it names integers or slices as the two acceptable things, which is a compact statement of this entire chapter.

7. Predict, Run, Observe

Predict all seven before reading on. Two of them are the distinctions from Sections 2 and 3.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
seeds = [4210, 9124, 77]
names = ["clk", "reset_n", "valid"]
print(seeds[0])
print(seeds[-1])
print(seeds[0:2])
print(type(seeds[0]))
print(type(seeds[0:1]))
print(names[1][0])
print(len(names[1]))

The verified output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
4210
77
[4210, 9124]
<class 'int'>
<class 'list'>
r
7

seeds[0] is the number 4210 — an int, because that is what the element is. seeds[0:1] is [4210], a list, which is why its type is list and not int. Those two lines are the whole of Section 3.

names[1][0] is "r": element 1 is "reset_n", and its first character is r. And len(names[1]) is 7 — the number of characters in "reset_n", not the number of elements in anything, because names[1] is a string.

If you predicted 3 for that last one you measured the list rather than the element. The expression len(names[1]) measures whatever names[1] evaluated to, which is the left-to-right rule from Section 5.

8. Exercises

Exercise 1 — reach the elements. For signals = ["clk", "reset_n", "valid", "ready"], write expressions producing: the first signal, the last signal, the middle two as a list, and everything except the first.

Exercise 2 — predict the type. Without running anything, give both the value and the type of each, then verify: (a) signals[0] (b) signals[0:1] (c) signals[:] (d) signals[1][0] (e) len(signals[1])

Exercise 3 — element or list. Explain in two sentences why ["PASS"] == "PASS" is False, and say which of results[0] and results[0:1] you would use to compare a status against "PASS".

Exercise 4 — find the off-by-one. This is meant to print the last seed and raises. Give the valid index range for this list and two different corrected versions:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
seeds = [4210, 9124, 77]
print(seeds[3])

Exercise 5 — the empty list. Explain in one sentence why failures[0] is risky in a script that reports test failures, and name the error it raises when nothing failed.

Exercise 6 — two brackets. For rows = ["smoke PASS", "reset FAIL"], predict the result of rows[1][:5] and rows[0][-4:], then verify. Say in one sentence what each of the two brackets is counting.

Exercise 7 — slice boundaries. For a list of ten runtimes, write the slice that gives the first three, the slice that gives the last three, and the slice that gives everything except the first and last. Do not use len() in any of them.

9. Summary

  • Position rules are identical to strings. Zero-based, -1 is the last, len(x) - 1 is the last valid index. Chapters 4.4 and 4.5 own the derivation.
  • An index gives back the element itself, of whatever type it is — not a one-character anything.
  • A slice gives back a list, and tests[0] is therefore not the same value as tests[0:1]. One is the element; the other is a list holding it.
  • The slice rules transfer unchanged: omitted boundaries, negative boundaries, stop - start length, adjacent slices tile, out-of-range does not raise.
  • tests[:] makes an independent copy of a list — a full slice meant little for strings and means a lot here. Chapter 5.8 is where that matters.
  • Each bracket applies to whatever is on its left. In tests[0][0], the first counts elements and the second counts characters of the resulting string.
  • IndexError: list index out of range for a position that does not exist, including position 0 of an empty list.
  • TypeError: list indices must be integers or slices for a float — the message names both acceptable forms.

Reaching into a list is reading. The next chapter changes one.

Where this fits

Part of the Python curriculum.