Computer Programming 1 (Python) • Core Data Structures

List Basics — I can create lists, access/update elements by index, and explain common index errors.

Learn how Python lists let you store, access, and update collections of data using numbered positions.

A Python coding terminal with a program flow and laptop.
Inside this lesson 10 sections 5 questions 8 figures

SECTION 1

Historical Context & Motivation

Imagine you need to store the scores of every student in a class. You could create a separate variable for each score—score1, score2, score3—but that quickly becomes unmanageable when you have thirty students. Programming languages solved this problem by introducing data structures that group multiple values under a single name. The most fundamental of these structures is the list, which keeps items in a defined order and lets you look up any item by its position number, called an index.

1958 • LISP Introduces Lists

John McCarthy created LISP ("LISt Processing"), one of the first programming languages built entirely around the concept of lists. The idea that a program could store and manipulate ordered collections became foundational to computer science.

1972 • C Brings Arrays

The C programming language popularized arrays—fixed-size, indexed collections stored in contiguous memory. Arrays required programmers to declare the size in advance, which was efficient but inflexible.

1991 • Python Lists Arrive

Guido van Rossum released Python with a built-in list type that combined the indexing power of arrays with the flexibility to grow, shrink, and hold mixed data types—all with beginner-friendly syntax.

Today • Lists Everywhere

Nearly every modern language includes some form of dynamic list. In Python, lists are one of the most-used data structures for tasks ranging from web development to data science and game programming.

The central question this lesson answers is straightforward: how do you store multiple values together, retrieve any single value quickly, change values in place, and avoid the most common mistakes that crash your program? By the end, you will be comfortable creating Python lists, reading and writing elements by index, and diagnosing IndexError messages.

SECTION 2

Core Principles & Definitions

Before writing any code, it helps to nail down the vocabulary. A list in Python is an ordered, mutable collection of items enclosed in square brackets. Each item sits at a numbered position called an index. Python uses zero-based indexing, meaning the first element is at index 0, the second at index 1, and so on. Understanding these four core ideas will unlock everything else in this lesson.

1

Creating a List

Use square brackets [] and separate items with commas. Example: colors = ["red", "blue", "green"]. Lists can hold integers, strings, floats, or even other lists.
2

Accessing by Index

Place the index inside square brackets after the list name: colors[0] returns "red". Negative indices count from the end, so colors[-1] returns "green".
3

Updating by Index

Assign a new value directly: colors[1] = "yellow" replaces "blue" with "yellow". This is possible because lists are mutable (changeable).
4

Index Errors

If you use an index that doesn't exist—like colors[5] on a 3-element list—Python raises an IndexError. The valid range is 0 through len(list) − 1.
✦

KEY TAKEAWAY

Think of a list like a row of school lockers. Each locker has a number on it (the index), starting at 0 instead of 1. You can peek inside any locker by calling its number, swap what's inside, or accidentally try to open a locker number that doesn't exist—which is exactly what an IndexError is.

SECTION 3

Visual Explanation — How a List Is Laid Out

Python List: scores = [92, 87, 74, 95, 88]Each box is one element. The number above is the positive index; below is the negative index.092−5187−4274−3395−2488−1ElementAccessing & Updatingscores[0] → 92scores[-1] → 88scores[2] = 80 # changes 74 → 80scores[5] → IndexError! ❌
The diagram above shows the list scores with five elements. Positive indices (cyan, violet, amber, emerald, orange numbers above each box) start at 0 from the left. Negative indices (pink numbers below) start at −1 from the right. The bottom section demonstrates accessing, updating, and the error that occurs when the index is out of range.

Notice how the positive index of the last element is always len(scores) − 1, which equals 4 for a five-element list. This "off-by-one" detail is the single biggest source of IndexError bugs for beginners. Negative indexing provides a convenient shortcut: -1 always reaches the last element regardless of how long the list is, -2 grabs the second-to-last, and so on.

SECTION 4

How Indexing Works Under the Hood

While you don't need to memorize low-level details, knowing the rules behind indexing will help you predict Python's behavior. The valid positive indices for a list of length n run from 0 to n − 1. The valid negative indices run from −n to −1. Any integer outside those two ranges triggers an IndexError.

VALID POSITIVE INDEX RANGE

0≤index≤len(list)−10 ≤ index ≤ len(list) − 1

For a list with n elements, the largest legal positive index is n − 1. Using n itself (or anything larger) causes an IndexError.

VALID NEGATIVE INDEX RANGE

−len(list)≤index≤−1−len(list) ≤ index ≤ −1

Negative indices wrap around from the end. −1 is the last element, −n is the first. Going below −n also causes an IndexError.

NEGATIVE-TO-POSITIVE CONVERSION

actualposition=len(list)+negativeindexactual_position = len(list) + negative_index

Python internally converts a negative index by adding the list length. For example, if len(list) = 5 and the index is −2, the actual position is 5 + (−2) = 3.

⚠

Common Pitfall: Off-by-One Errors

Because indexing starts at 0, a list with 5 elements has indices 0 through 4—not 1 through 5. If you write my_list[len(my_list)], you will always get an IndexError. The correct last-element access is my_list[len(my_list) − 1] or simply my_list[-1].

SECTION 5

Common Index Errors & How to Fix Them

When Python encounters an invalid index, it halts your program and prints a traceback ending with IndexError: list index out of range. This message tells you the index you used doesn't match any position in the list. Let's categorize the three most common scenarios and their fixes.

Three Common Index Error Scenarios❶ Index Too Largefruits = ["a","b","c"]print(fruits[3])IndexError!Valid: 0, 1, 2Used: 3 (doesn't exist)Fix: use index 2 or −1❷ Empty List Accessdata = []print(data[0])IndexError!Valid: none (empty)Used: 0 (nothing there)Fix: check len(data) > 0❸ Loop Off-by-Onenums = [10, 20, 30]for i in range(4): print(nums[i])Crashes when i = 3range(4) → 0,1,2,3Fix: range(len(nums))Defensive Coding Strategies1Always use range(len(my_list)) in loops to match list size automatically.2Check if the list is non-empty before accessing: if len(my_list) > 0.3Use negative indices (−1) to safely grab the last element without knowing length.4Print len(my_list) while debugging to confirm the actual size of your data.
This diagram shows three common IndexError scenarios side by side (red, amber, and violet panels) along with their fixes. The bottom section lists four defensive coding strategies that help prevent these errors before they occur.
Error ScenarioExample CodeWhy It FailsQuick Fix
Index too largelst[len(lst)]Max valid index is len(lst) − 1lst[len(lst) - 1] or lst[-1]
Empty list[][0]No elements exist at any indexCheck if len(lst) > 0 first
Negative too farlst[-6] (5 items)Negative limit is −len(lst)Ensure abs(index) ≤ len(lst)
Loop overshootsrange(len(lst) + 1)Last iteration exceeds max indexUse range(len(lst))

Summary of IndexError scenarios and fixes

SECTION 6

Worked Example — Building a Grade Tracker

Let's walk through a realistic mini-program step by step. You are building a simple grade tracker that stores quiz scores, updates a score after a regrade, and prints the first and last score.

Grade Tracker: Create, Access, and Update

  1. 1

    Step 1 — Create the List

    Start by defining a list of five quiz scores. We use square brackets and commas to separate each integer value. grades = [88, 72, 95, 64, 91]
    List created: [88, 72, 95, 64, 91] with length 5
  2. 2

    Step 2 — Access the First Score

    To display the first quiz score, use index 0. Remember, Python starts counting positions from zero. print(grades[0])
    Output: 88
  3. 3

    Step 3 — Access the Last Score Using Negative Indexing

    Instead of calculating len(grades) - 1 (which equals 4), simply use −1 to grab the final element. print(grades[-1])
    Output: 91
  4. 4

    Step 4 — Update a Score After a Regrade

    The student earned extra points on quiz 4 (index 3). Assign the new value directly to that index. grades[3] = 78 print(grades)
    Updated list: [88, 72, 95, 78, 91] — index 3 changed from 64 to 78
  5. 5

    Step 5 — Attempt an Invalid Index (Debugging)

    What happens if we accidentally try to access the sixth element (index 5)? print(grades[5]) Python raises IndexError: list index out of range. The highest valid index is 4 (five elements, indices 0–4). We fix it by using grades[4] or grades[-1].
    Takeaway: always verify your index is between 0 and len(list) − 1.

SECTION 7

Lists vs. Other Ways to Store Data

Lists aren't the only option for holding multiple values in Python. Understanding when a list is the right tool—and when it isn't—will make you a stronger programmer. The table below compares lists with two alternatives you may encounter: tuples (immutable sequences) and individual variables.

FeatureListTupleSeparate Variables
Syntax[1, 2, 3](1, 2, 3)a=1; b=2; c=3
Mutable?Yes — can change elementsNo — fixed after creationEach variable is independent
Indexed?Yes, 0-basedYes, 0-basedNo shared index
Can grow/shrink?Yes (append, remove)NoMust create new variables
Best forCollections you'll modifyFixed data (coordinates, RGB)Few unrelated values

Comparison of lists, tuples, and separate variables

📋

WHEN TO USE A LIST

Choose a list whenever you have a collection of related items that might change—like quiz scores, usernames, or inventory items. If the data should never change (like the x-y coordinates of a fixed point), a tuple is safer. And if you only have two or three unrelated values, separate variables are fine.

SECTION 8

Connection to Advanced Concepts

The skills you've learned here—creating, accessing, and updating lists by index—are the foundation for almost every data structure you'll encounter later in your computer science journey. The table below previews how today's concepts connect to more advanced topics.

Today's ConceptAdvanced ExtensionWhy It Matters
Creating a listList comprehensionsBuild lists from formulas in a single line, e.g., [x**2 for x in range(10)]
Accessing by indexSlicingGrab sub-lists: lst[1:4] returns elements at indices 1, 2, 3
Updating by indexMethods like append, insert, popAdd and remove elements dynamically, not just overwrite
IndexError awarenessException handling (try/except)Gracefully recover from errors instead of crashing
Indexed collectionsDictionaries and 2D listsStore data by keys instead of positions, or create grids (lists of lists)

How today's list basics connect to future topics

Every advanced data structure builds on the same core idea: store data in an organized way and retrieve it efficiently. Once you are comfortable with list indexing, techniques like slicing, list comprehensions, and 2D lists will feel like natural extensions rather than entirely new concepts.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

A list has 7 elements. What is the index of the first element and the index of the last element using positive indexing? Why does accessing index 7 cause an error?
Show answer
The first element is at index 0 and the last element is at index 6. Python uses zero-based indexing, so a list of length 7 has valid indices 0 through 6. Accessing index 7 causes an IndexError because there is no eighth position—the highest valid positive index is always len(list) − 1, which is 7 − 1 = 6.

Question 2: BASIC CALCULATION

Given temps = [68, 72, 75, 70, 66], what does each of the following expressions evaluate to? (a) temps[2] (b) temps[-2] (c) temps[0]
Show answer
(a) temps[2] returns 75 because the element at index 2 (the third element) is 75. (b) temps[-2] returns 70 because −2 refers to the second-to-last element. Using the conversion formula: 5 + (−2) = 3, and temps[3] is 70. (c) temps[0] returns 68, the first element.

Question 3: INTERMEDIATE

You run the following code. What is printed, and why? animals = ["cat", "dog", "fish", "bird"] animals[1] = "hamster" animals[-1] = "parrot" print(animals)
Show answer
The output is ["cat", "hamster", "fish", "parrot"]. The first assignment replaces index 1 ("dog") with "hamster". The second assignment targets index −1, which is the last element ("bird"), and replaces it with "parrot". Index 0 ("cat") and index 2 ("fish") are unchanged because they were never reassigned. This demonstrates that lists are mutable—you can overwrite any element by assigning to its index.

Question 4: APPLIED

You're writing a program to track daily step counts for a week: steps = [8200, 6500, 9100, 7400, 10300, 5600, 11200]. Write code that (a) prints Thursday's step count, (b) corrects Friday's count to 10500, and (c) prints the weekend's step counts (Saturday and Sunday) using negative indexing.
Show answer
(a) Thursday is the fourth day, so it's at index 3: print(steps[3]) outputs 7400. (b) Friday is index 4: steps[4] = 10500. (c) Saturday is the second-to-last day (steps[-2] = 5600) and Sunday is the last day (steps[-1] = 11200). You could write print(steps[-2], steps[-1]). Negative indexing is convenient here because you don't need to know the exact list length.

Question 5: CRITICAL THINKING

A classmate writes a function that takes a list and an index as inputs and is supposed to print the element at that index. However, the function sometimes crashes. Explain what could go wrong, and rewrite the function so that it prints a friendly error message instead of crashing. Hint: use len() and an if/else statement.
Show answer
The function crashes when the index is out of range—either because it's ≥ len(list) for positive indices or < −len(list) for negative ones. A defensive version checks validity first: def safe_access(lst, idx): if -len(lst) <= idx <= len(lst) - 1: print(lst[idx]) else: print("Error: index", idx, "is out of range for a list of length", len(lst)) This approach uses the valid range formula (−len ≤ idx ≤ len − 1) to decide whether the access is safe. It prevents the IndexError by catching invalid indices before Python tries to use them, which is a pattern called defensive programming.

SUMMARY

Lesson Summary

Summary

A Python list is an ordered, mutable collection created with square brackets and commas. Each element sits at a numbered position called an index, and Python uses zero-based indexing, so the first element is at index 0 and the last at index len(list) − 1. You can access any element with list[index] and update it by assigning a new value to that same position.

Negative indices count from the end of the list, with −1 as the last element. An IndexError occurs whenever you use an index outside the valid range. The three most common causes are using an index equal to the list length, accessing an element in an empty list, and letting a loop variable exceed the maximum index. To prevent these bugs, use range(len(list)) in loops, check list length before accessing, and prefer negative indexing when you need the last element.

Varsity Tutors • Computer Programming 1 (Python) • List Basics — I can create lists, access/update elements by index, and explain common index errors.