Grade by Grade
A. Programming Concepts and Skills · Arrays and Subprograms

Lists as One-Dimensional Arrays: Elements, Indexes and Bounds

A1.5 A1.6 A4.3About 70 minutes
Learning goals
  • I can explain what a one-dimensional array is and why programs need one.
  • I can use the words element, index, length, lower bound and upper bound correctly.
  • I can find any element of a Python list using a positive or a negative index.
  • I can predict when an index is out of bounds and read the IndexError message Python gives.

Words to know

array (one-dimensional)
An ordered collection of values stored under one name, in a single row. Each value is found by its position number.
list
Python's built-in ordered collection, written with square brackets, e.g. [4, 8, 15]. In this course a Python list plays the role of a one-dimensional array.
element
One value stored in an array. In [4, 8, 15] the elements are 4, 8 and 15.
index (subscript)
The position number of an element. Python indexes start at 0, so the first element is at index 0.
length
The number of elements in an array. In Python, len(a) gives the length of a.
bounds
The smallest and largest legal indexes. For a Python list of length n the lower bound is 0 and the upper bound is n − 1.
IndexError
The run-time error Python raises when a program uses an index that is outside the bounds of a list.

One name, many values

Imagine you are tracking the morning temperature in your town for one week in January. With what you learned in Programming Basics, you might write:

mon = -8, tue = -12, wed = -5 ... and four more variables.

That works for 7 days. Now track a whole winter (about 90 days), or every student's mark in a school of 1200. You would need hundreds of variable names, and you could not loop through them. Programmers solve this with an array: one name that holds many values in order, where each value is found by its position number.

Remember from Programming Basics

A variable holds one value of one data type (int, float, str or bool). A for loop with range() repeats code, and range(n) gives 0, 1, 2, ..., n − 1. You may also have met run-time errors: errors that only appear while the program is running. Today all of these come together.

What is a one-dimensional array?

A one-dimensional array is a single row of values stored under one name. Each value is an element. Each element has a position number called its index (or subscript).

In languages such as Java and C++, an array has a fixed size that you choose when you create it, and every element has the same data type. Python's built-in list plays the role of an array. A list is more flexible: it can grow and shrink, and it is even allowed to mix types. In this course we will use lists the way arrays are used everywhere else: one list holds one kind of data (all temperatures, all names, all marks).

You create a list with square brackets and commas:

temps = [-8, -12, -5, 0, 3, -2, -6]
print(temps)
print(len(temps))

Output: [-8, -12, -5, 0, 3, -2, -6] and then 7. The function len() gives the length: how many elements the list has.

Indexes start at 0

To use one element, write the list name, then the index in square brackets. The first element is at index 0, not 1. So in a list of 7 elements the indexes are 0, 1, 2, 3, 4, 5 and 6. The last index is always one less than the length.

tempsnegative indexelementindex-80-7-121-6-52-503-434-3-25-2-66-1
The list temps has 7 elements. The bottom numbers are the indexes 0 to 6. The top numbers are Python's negative indexes, which count back from the end: −1 is the last element and −7 is the first. The first and last elements are shaded.
temps = [-8, -12, -5, 0, 3, -2, -6]
print(temps[0])
print(temps[2])
print(temps[6])
print(temps[-1])
print(temps[len(temps) - 1])

Output, one per line: -8, -5, -6, -6, -6. The last three lines all reach the last element in three different ways.

Why start at 0?

In languages like C and Java, the elements of an array sit side by side in memory, each taking the same number of bytes. The computer only remembers where the array starts. The index tells it how many elements to skip from the start. Element 0 is zero steps from the start, element 1 is one step, and so on. That makes finding any element instant: one multiplication and one addition, no matter how long the array is.

An int array in Java or C: 4 bytes per element23address 1000index 08address 1004index 115address 1008index 242address 1012index 34address 1016index 4address of element i = 1000 + 4 × i (element 3: 1000 + 12 = 1012)
An array of 5 integers that starts at memory address 1000. Each int takes 4 bytes, so element i is at address 1000 + 4 × i. Element 3 (shaded) is at 1012. (The addresses are an example; real ones are much larger numbers.)

A Python list works on the same idea: inside the computer it keeps a row of references (arrows) to its values side by side, so temps[500] is just as fast as temps[0].

Bounds

The bounds of an array are its smallest and largest legal indexes.

  • Lower bound: 0
  • Upper bound: length − 1, which is len(a) - 1

Python also accepts negative indexes from −1 down to −length. Any index outside these ranges is out of bounds. There is no element there, so Python stops the program with an IndexError. This is a run-time error: the code has correct grammar (syntax), so it starts running, and it only fails when it reaches the bad index.

14029172913884??-65lower bound: 0upper bound: len − 1 = 4out of boundsout of bounds
A list of length 5. Legal indexes are 0 to 4 (and −1 to −5). Index 5 and index −6 (dashed boxes) do not exist, so using them raises an IndexError.
temps = [-8, -12, -5, 0, 3, -2, -6]
print(temps[6])
print(temps[7])

The first print works. The second asks for index 7, but the upper bound is 6.

-6
Traceback (most recent call last):
File "snow.py", line 3, in <module>
print(temps[7])
~~~~~^^^
IndexError: list index out of range

What appears when the program is saved as snow.py (Python 3.11 and later; older versions leave out the line of ~ and ^ marks). Read an error message from the bottom up: the last line names the error; the lines above say which file and line number caused it.

Reading an error message

An error message like the one above is called a traceback. Read it from the bottom:

  1. The last line gives the error type and a short message: IndexError: list index out of range.
  2. The line above it shows the exact code that failed: print(temps[7]). The ^ marks point at the part that went wrong.
  3. line 3 tells you where to look in your file.

When you see IndexError, ask: How long is the list? What index did I use? Is it bigger than length − 1?

Worked example

Example 1: Working with the indexes of a list

A hockey team stores its jersey numbers: jerseys = [91, 16, 34, 44, 8, 29].

(a) What is the length? (b) What are the lower and upper bounds? (c) What is jerseys[3]? (d) Give two different indexes that both reach 29. (e) What happens with jerseys[6]?

  1. (a) Count the elements: 91, 16, 34, 44, 8, 29. There are 6, so len(jerseys) is 6.
  2. (b) Lower bound 0. Upper bound 6 − 1 = 5.
  3. (c) Count from 0: index 0 is 91, 1 is 16, 2 is 34, 3 is 44.
  4. (d) 29 is the last element, so it is at index 5 and also at index −1.
  5. (e) 6 is bigger than the upper bound 5, so Python raises IndexError: list index out of range.

Length 6; bounds 0 to 5; jerseys[3] is 44; 29 is at index 5 and −1; jerseys[6] is out of bounds.

Worked example

Example 2: Off-by-one

Sam wants to print the last of 30 quiz marks stored in marks. Sam writes print(marks[30]). Explain the bug and fix it in a way that still works if the class gets bigger.

  1. A list of 30 elements has indexes 0 to 29. Index 30 is one past the end. This mistake is so common it has a name: an off-by-one error.
  2. A fixed fix is marks[29], but it breaks if a 31st mark is added.
  3. A fix that always works: marks[len(marks) - 1], or the Python shortcut marks[-1].

Use print(marks[-1]) or print(marks[len(marks) - 1]).

Just for fun

Why do programmers mix up Halloween and Christmas?

Because Oct 31 = Dec 25. (In base 8, "octal", 31 means 3 × 8 + 1 = 25.) Programmers count from strange places, and starting arrays at 0 is only the beginning.

Hands-on activity

A kitchen-cupboard array

You need: 6 mugs or cups in a row, 6 small slips of paper, a pen, sticky notes or tape, and a computer with Python (or a free browser-based Python such as the one at python.org/shell).

  1. Put a sticky note in front of each mug with its index: 0, 1, 2, 3, 4, 5.
  2. Write a number on each slip (for example, today's high temperature for 6 different Canadian cities from a weather site) and put one slip in each mug. The mugs are your array; the slips are the elements.
  3. Ask a family member to call out an index. You "access" that mug and read the element. Have them call out −1, −3 and 6 too. What should happen for 6?
  4. Type the same list into Python, e.g. highs = [4, -3, 11, 7, -1, 2]. Test every index they called, including the out-of-bounds one, and read the error message from the bottom up.

What to look for: the student says "index 0" for the first mug without being reminded, and predicts the IndexError before running the code.

Your turn

1

For days = ["Mon", "Tue", "Wed", "Thu", "Fri"], what is len(days)?

2

For the same list, what is days[1]?

3

What are the lower and upper bounds of days?

4

What is days[-2]?

5

What happens with print(days[5])? Is it a syntax error or a run-time error?

6

Write one expression that gives the last element of any list a, without using a negative index.

7

A list has 250 elements. What is its upper bound? What negative index reaches the first element?

More practice: warm-up

1

What symbol surrounds a list in Python?

2

Name the value at index 0 of [7, 3, 9].

3

How many elements does [] have?

4

What is len(["maple", "oak", "birch", "spruce"])?

5

True or false: in Python, the first element of a list is at index 1.

More practice: on your own

1

Predict the output:

snow = [12, 0, 5, 30, 2]

print(snow[3] - snow[1])

2

Predict the output:

snow = [12, 0, 5, 30, 2]

print(snow[-1] + snow[-5])

3

Predict the output:

snow = [12, 0, 5, 30, 2]

i = len(snow) // 2

print(i, snow[i])

4

A list names holds 18 names. Which of these indexes are legal: 0, 18, 17, −18, −19?

5

This line gives IndexError: print(names[len(names)]). Explain why it always fails, for any non-empty list.

Challenge

1

An array of 64-bit floats (8 bytes each) starts at memory address 5000 in C. At what address is element 25? Which index is stored at address 5200?

2

For a list a of length n (n ≥ 1), write a rule that changes a negative index k (from −1 to −n) into the matching positive index. Test it with k = −1 and k = −n.

3

Explain why a[0] raises an IndexError when a = [], but len(a) does not.

Check your understanding

Quiz

1.

In the list [5, 10, 15, 20], what is the index of 15?

2.

What is the upper bound of a list of length 12?

3.

For pets = ["cat", "dog", "fish"], what is pets[-1]?

4.

What kind of error is IndexError: list index out of range?

5.

Which expression always gives the last element of a non-empty list a?

6.

Why do array indexes start at 0 in languages like C and Java?

For parents and teachers

Big idea: Expectation A1.5 asks students to describe the structure of one-dimensional arrays, including elements, indexes and bounds. In Python we use the built-in list as the array. A4.3 (reading error messages) starts here with the IndexError traceback.

How to help: Ask your teen to explain the mug activity back to you using the words element, index and bound. Name a list of real things (the family's birthdays, a week of temperatures) and ask, "What would be at index 0? What is the last index?"

Common difficulties:

  • Counting from 1. Most beginners say the third element is at index 3. Practise saying "index 2, the third element".
  • Off-by-one errors: using len(a) as an index. The last index is len(a) - 1.
  • Panicking at error messages. Teach the habit of reading the last line first, then the line number.
  • Negative indexes are a Python feature. Other languages (Java, C++) do not allow them, so students should understand both a[-1] and a[len(a) - 1].