South Plantation High School logo
South Plantation High School · Procedural Programming with Python

Lists: Indexing & Slicing

Creating lists · Positive & negative indexing · Slice notation · Lesson review

Review the lesson slides, then complete the assignment at the bottom of this page. Your assignment work is saved in this browser on this computer. When you check a task, your name, period, and which tasks are complete are shared with your teacher (your code is not). When you finish, print it or save it as a PDF to turn in.
1 / 17
Lesson Review

Lists: Indexing & Slicing

[10, 20, 30]nums[0]nums[-1]nums[1:3]nums[::-1]

Store many values in one variable, then pull out exactly the ones you need.

By the end of this lesson you will be able to:

  • Create lists with square brackets, including empty and mixed-type lists.
  • Access items with positive indexing (counting from the front, starting at 0).
  • Access items with negative indexing (counting from the back, starting at -1).
  • Extract sublists with slice notation: list[start:stop:step].
How to use this page: Use Next and Back (or the arrow keys). Click the boxes in the index ruler, drag through slices in the slice visualizer, and take on the index and slice challenges. Every result comes from running real Python rules.

Words you will hear today

WordWhat it meansExample
listAn ordered collection of values in one variable[10, 20, 30]
item / elementOne value inside a list20
indexAn item's position numbernums[0]
sliceA new list made from a range of positionsnums[1:3]
mutableCan be changed after it is createdlists are mutable
IndexErrorThe error for an index that does not existnums[99]
  • Create, index (positive and negative), slice.
  • Item, index, slice, mutable, IndexError.

So far, each variable has held one value. Today we learn about lists, which hold many values in order. Then we learn the two ways to get values back out: indexing, which grabs one item, and slicing, which grabs a group of items.

Creating Lists

What is a list?

A list stores many values, called items (or elements), in order, inside one variable. The Python tutorial describes it like this:

"The most versatile is the list, which can be written as a list of comma-separated values (items) between square brackets."
  • Square brackets [ ] start and end the list.
  • Commas separate the items.
  • The order you write them in is the order they stay in.
  • len(list) tells you how many items it has.
Without a list: one variable per score
score1 = 88
score2 = 92
score3 = 75
With a list: one variable holds them all
scores = [88, 92, 75]
print(scores)        # [88, 92, 75]
print(len(scores))   # 3
print(type(scores))  # <class 'list'>
Think of a list like a row of numbered lockers. Each locker holds one item, and each has a number (its index) so you can find it.

Why lists make programs better

  • They grow. With separate variables, a 31st student means writing score31 and changing your code. A list can just get one more item.
  • They work with loops. for score in scores: handles 3 scores or 3,000 with the same two lines.
  • They work with built-in tools. sum(scores), max(scores), and min(scores) work on a whole list at once.
scores = [88, 92, 75]
print(sum(scores) / len(scores))   # the average: 85.0
  • List = ordered collection of items in [ ], separated by commas.
  • len() counts the items.
  • Lists grow, work with loops, and work with sum, max, min.

Imagine storing the scores for a class of thirty students with thirty separate variables. A list lets you keep all of them together under one name, in order. The brackets mark the beginning and end, and the commas separate the items.

Creating Lists

Creating lists

KindExample
Numbersscores = [88, 92, 75]
Stringsnames = ["Ava", "Ben", "Cy"]
Empty listcart = [] (length 0)
Mixed typesinfo = ["Ava", 15, 3.8, True]
From a rangelist(range(5)) → [0, 1, 2, 3, 4]
From a stringlist("hey") → ['h', 'e', 'y']
A list inside a listgrid = [[1, 2], [3, 4]]
Lists can mix types, but the tutorial notes that "usually the items all have the same type." A list of all scores or all names is easier to work with.
Forgot a comma? [1 2 3] gives SyntaxError: invalid syntax. Perhaps you forgot a comma?

Pick a way to create items and run it. Watch the length and the type.

Reading what Python prints

When Python prints a list, it shows strings with quotes: ['h', 'e', 'y']. That is how you can tell the difference between the list [1, 2] (numbers) and ['1', '2'] (strings). The quotes are not part of the values; they just show the type.

Spreading a list over several lines

Long lists can be written over several lines. Python keeps reading until it finds the closing ]:

menu = [
    "pizza",
    "tacos",
    "salad",
]

The comma after the last item is allowed, and it makes it easy to add another item later.

  • [], list(range()), list("text"), mixed, nested.
  • Missing comma is a SyntaxError.
  • Printed lists show quotes around strings so you can see the type.

Here are the common ways to create a list. The empty list is useful when you plan to add items later. Notice that Python prints strings inside a list with quotes around them, so you can tell the string "5" from the number 5. Try the last option to see the missing-comma error.

Indexing

Every item has two index numbers

  • An index is an item's position number. You put it in square brackets after the list name: colors[2].
  • Positive indexes count from the front, starting at 0.
  • Negative indexes count from the back, starting at -1.
Why does it start at 0? Think of the index as "how many steps from the front." The first item is 0 steps from the front.
Shortcut: for a list of length n, the negative index is the positive index minus n. In a 6-item list, index 2 is the same item as index 2 - 6 = -4.

Click any box to see both of its indexes, or type an index to try.

Why the last index is one less than the length

A list with 6 items has indexes 0, 1, 2, 3, 4, 5. That is still 6 numbers, but because the counting started at 0, it ends at 5. So the last index is always length − 1.

A handy trick: the middle item

len(items) // 2 gives the index of the middle item. For a 5-item list, 5 // 2 is 2, the third item, which has 2 items on each side. Use //, not /, because an index must be a whole number.

  • Positive: 0 to len - 1. Negative: -1 to -len.
  • negative = positive - len.
  • Last index = len - 1. Middle index = len // 2.

Every item in a list has two addresses. From the front, the first item is zero. From the back, the last item is negative one. Click a few boxes and look at both numbers. Then type an index that is too big, like 6, and see what Python says.

Indexing

Positive indexing: counting from the front

scores = [88, 92, 75, 64, 99]
#  index:   0   1   2   3   4

print(scores[0])   # 88  (first item)
print(scores[2])   # 75  (third item)
print(scores[4])   # 99  (last item)
print(scores[5])   # IndexError!
  • The first item is [0], so the second item is [1]. The item number is always one more than its index.
  • The last positive index is len(scores) - 1. Here that is 4.
  • An index past the end gives IndexError: list index out of range.
  • The index must be an int. scores[1.0] gives TypeError: list indices must be integers or slices, not float.

Change the index and run it. What is the biggest index that works?

Using a variable as the index

The number inside the brackets can be any expression that gives an int, including a variable or math:

pick = 2
print(scores[pick])        # 75
print(scores[pick + 1])    # 64
print(scores[len(scores) - 1])   # 99, the last item

This is what makes loops with indexes possible: for i in range(len(scores)): then scores[i] visits every position.

  • First is [0]. Last is [len - 1].
  • Too big: IndexError. Not an int: TypeError.
  • The index can be a variable or an expression.

The most common list bug is forgetting that counting starts at zero. If you want the third score, you ask for index 2. And because the first index is zero, the last index is one less than the length. Five items means the last index is four.

Indexing

Negative indexing: counting from the back

scores = [88, 92, 75, 64, 99]
#  index:  -5  -4  -3  -2  -1

print(scores[-1])   # 99  (last item)
print(scores[-2])   # 64  (second to last)
print(scores[-5])   # 88  (first item)
print(scores[-6])   # IndexError!

The tutorial explains why it starts at -1: "Note that since -0 is the same as 0, negative indices start from -1."

Why it is useful: scores[-1] always gets the last item, no matter how long the list is. You do not need to know the length.
To get the...PositiveNegative
first itemscores[0]scores[-5]
last itemscores[4] or scores[len(scores) - 1]scores[-1]
second to lastscores[3]scores[-2]

How Python converts a negative index

Python adds the length: for a 5-item list, scores[-2] becomes scores[5 + (-2)], which is scores[3]. That is why the most negative index that works is -len: it turns into 0.

When to use which: use positive indexes when you count from the start ("the first three players"). Use negative indexes when you care about the end ("the most recent score," "the last item added").

  • -1 is last, -2 second to last, -len is first.
  • Too far back: IndexError.
  • Negative index i becomes len + i.

Negative indexes count backward from the end. Negative one is always the last item, which is very handy because lists often change size. Negative two is the second to last, and so on, until you reach the first item at negative five.

Practice

Index challenge

Find the item using both a positive and a negative index, then click Check. Practice only; nothing is recorded.

Strategy

  1. Write the positive indexes above the items, starting at 0.
  2. Write the negative indexes below, starting at −1 on the right.
  3. Check: for any item, positive − negative should equal the length.
  • Count from 0 at the front, from -1 at the back.

Use your fingers if it helps. Point to the first planet and say zero, then count forward. For the negative index, point to the last planet and say negative one, then count backward. Check your answer, then click New item and try again.

Indexing

Using an index to change an item

Lists are mutable: you can change their items. Put the index on the left side of =:

colors = ["red", "green", "blue"]
colors[0] = "orange"      # replace the first item
colors[-1] = "purple"     # replace the last item
print(colors)             # ['orange', 'green', 'purple']
  • Shortcut operators work too: scores[1] += 5.
  • You can only replace an item that exists. colors[5] = "pink" gives IndexError: list assignment index out of range.
Strings can be indexed but not changed. The tutorial: "Unlike strings, which are immutable, lists are a mutable type." So word[0] = "b" gives TypeError: 'str' object does not support item assignment.

Step through and watch colors change in the Variables panel. The last line shows what happens with a string.

Swapping two items

Python can swap two items in one line. The right side is worked out first, then both values are stored:

colors = ["red", "green", "blue"]
colors[0], colors[2] = colors[2], colors[0]
print(colors)    # ['blue', 'green', 'red']

Adding a new item? You cannot assign to an index that does not exist yet. To add to the end, use colors.append("pink"), which you will learn with list methods.

Why can lists change but strings cannot? Lists are made to be updated as a program runs (a shopping cart, a high-score table). Strings are treated as single values, like numbers, so they stay fixed.

  • list[i] = value replaces an item.
  • Strings are immutable: no item assignment.
  • a[i], a[j] = a[j], a[i] swaps two items.

Indexing does not only read items. It can also replace them. Put the index on the left of the equals sign, and the new value on the right. Lists let you do this because they are mutable, which means changeable. Strings are not, so the last line in the editor causes an error.

Slicing

Slicing: list[start:stop]

A slice gets a sublist: a new list holding a range of items.

  • start is the index of the first item you want.
  • stop is the index where Python stops. That item is not included.
  • The number of items is stop - start.
The tutorial: "Note how the start is always included, and the end always excluded."
letters = ["a", "b", "c", "d", "e", "f"]
print(letters[1:4])   # ['b', 'c', 'd']  (4 - 1 = 3 items)
print(letters[0:2])   # ['a', 'b']
print(letters[3:4])   # ['d']  (a list with one item)

Pick a start and a stop. The highlighted boxes are the slice. Leave step blank for now.

Reading a slice step by step

For letters[1:4]:

  1. Start at index 1: 'b'. Include it.
  2. Index 2: 'c'. Include it.
  3. Index 3: 'd'. Include it.
  4. Index 4 is the stop. Stop before it.

Why leave out the stop? It makes the math easy: the number of items is just stop − start, and two slices that share a number fit together perfectly with nothing missing and nothing doubled: letters[0:3] + letters[3:6] is the whole list.

The slice is a new list. Changing it does not change the original list.

  • Start included, stop excluded.
  • Length = stop - start.
  • Slices that share a boundary fit together perfectly.

A slice uses a colon inside the brackets. The rule to remember is: start is in, stop is out. So letters one to four gives you indexes one, two, and three. A good way to think about it is that the slice stops right before the stop index.

Slicing

Leaving out start or stop

The tutorial: "an omitted first index defaults to zero, an omitted second index defaults to the size of the string being sliced." Lists work the same way.

SliceMeansWith letters (a to f)
letters[:3]from the start up to index 3['a', 'b', 'c'] (first 3)
letters[3:]from index 3 to the end['d', 'e', 'f']
letters[:]everythinga copy of the whole list
Neat fact: letters[:i] + letters[i:] always gives back the whole list, because the item at i goes in exactly one of the two halves.
A slice is a new list. The tutorial: "All slice operations return a new list containing the requested elements." So a[:] makes a copy, but b = a just gives the same list a second name.

What the demo shows

After a[0] = 99, both a and b show 99, because b = a did not make a copy. It made a second name for the same list, like two nicknames for one person. Only c, made with a[:], is a real copy and keeps the 1.

Remember: = with a list never copies it. If you want an independent list to change safely, use a[:] or a.copy().
  • [:n] first n items. [n:] from n to the end. [:] a copy.
  • b = a is the same list; a[:] is a new list.
  • b = a shares the list; a[:] copies it.

You can leave out either number. No start means from the beginning, and no stop means all the way to the end. Leave out both and you get a copy of the whole list. Step through the editor: when we change a, the name b changes too, because b is the same list. But c, the copy, does not.

Slicing

Negative indexes in slices

Negative indexes work in slices too. They are great for "the last few" or "all but the last."

SliceResult with lettersIn words
letters[-3:]['d', 'e', 'f']the last 3 items
letters[:-1]['a', 'b', 'c', 'd', 'e']all but the last item
letters[1:-1]['b', 'c', 'd', 'e']all but the first and last
letters[-4:-2]['c', 'd']start included, stop excluded
The same rule applies: start is in, stop is out. letters[-4:-2] starts at -4 ('c') and stops before -2 ('e').

Try negative starts and stops in the visualizer. Can you make ['e', 'f'] two different ways?

Two ways to say "the last 3"

For a 6-item list, letters[3:] and letters[-3:] give the same result. But only [-3:] keeps working if the list gets longer or shorter. Negative slices are the reliable way to get items from the end.

Answering the challenge

['e', 'f'] can be made with letters[4:], letters[-2:], letters[4:6], or even letters[-2:6]. You can mix positive and negative numbers in the same slice.

  • [-n:] last n items. [:-1] all but last. [1:-1] all but first and last.
  • [-n:] keeps working when the list length changes.

Negative slices are some of the most useful slices in Python. Negative three colon means the last three items, no matter how long the list is. Colon negative one means everything except the last item. Try to build the same slice two different ways, once with positive numbers and once with negative ones.

Slicing

The step: list[start:stop:step]

A third number, the step, says how far to jump each time. The default step is 1.

SliceResult with lettersIn words
letters[::2]['a', 'c', 'e']every other item
letters[1::2]['b', 'd', 'f']every other, starting at index 1
letters[::3]['a', 'd']every third item
letters[::-1]['f', 'e', 'd', 'c', 'b', 'a']the whole list, reversed
letters[4:1:-1]['e', 'd', 'c']backward from 4, stop before 1
A negative step goes backward, so start should be after stop. A step of 0 is an error: ValueError: slice step cannot be zero.

Now use the step box. Try step = -1 with start and stop left blank.

How a step slice picks items

For letters[1::2]: start at index 1 ('b'), then keep adding 2: index 3 ('d'), index 5 ('f'), index 7 is past the end, so stop.

Backward slices: with letters[4:1:-1], Python starts at 4 and subtracts 1 each time: 4, 3, 2. It stops before reaching 1. If the start were smaller than the stop with a negative step, the result would be empty, because you can never walk backward from a small number to a bigger one.

[::-1] makes a reversed copy. The original list stays in its order.

  • Step = jump size. [::2] every other. [::-1] reversed.
  • Step 0 is a ValueError.
  • A negative step walks backward; start must be bigger than stop.

The step is the jump size. A step of two skips every other item. A negative step walks backward, so colon colon negative one is a famous Python trick for reversing a list. With a negative step, the start needs to be to the right of the stop, because you are walking left.

Slicing

Index versus slice

Index nums[2]Slice nums[2:3]
Gives youone itema new list
Result30[30]
Out of rangeIndexErrorNo error. You get what exists (maybe [])

The tutorial: "Attempting to use an index that is too large will result in an error" but "out of range slice indexes are handled gracefully when used for slicing."

Strings slice the same way. "Python"[0] is 'P', "Python"[-1] is 'n', and "Python"[1:4] is 'yth'.

Run each option and compare the type of the result.

Why the type of the result matters

An index and a slice can look similar but give different things, and that changes what you can do next:

  • nums[2] + 5 works: 30 + 5 is 35.
  • nums[2:3] + 5 fails with a TypeError: you cannot add a number to a list.

Rule: no colon means one item. A colon means a list, even if that list holds only one item or none.

  • Index: one item, can IndexError.
  • Slice: a list, never IndexError.
  • No colon: one item. A colon: always a list.

Indexes and slices look alike, but they give back different things. An index gives the item itself. A slice always gives a list, even if it only has one item, or none at all. And slices are forgiving: if you go past the end, Python just gives you whatever items exist.

Practice

Slice challenge

Type a slice in brackets (like [0:2]) that produces each target list, then click Check. There is often more than one right answer. Practice only; nothing is recorded.

Strategy

  1. Find the index of the first item you want: that is your start.
  2. Find the index of the last item you want and add 1: that is your stop (with a step of 1).
  3. Need every other item, or a reversed list? Add a step.
  4. Check your answer by counting: does stop − start (with step 1) equal the number of items in the target?
  • Many answers can be correct: [0:3] and [:3] are the same slice.

For each target, find where it starts and where it ends. Remember that the stop index is one past the last item you want. For the reversed ones, use a negative step. If you get one right, try to find a second slice that also works.

Debugging

Common mistakes

MistakeCodeWhat happens (Python 3.13)Fix
Thinking the first index is 1names[1] for the first nameNo error. You get the second namenames[0]
Using the length as an indexnames[len(names)]IndexError: list index out of rangenames[-1]
Expecting the stop itemnums[1:3] for 3 itemsNo error. Only 2 itemsnums[1:4]
Expecting an item from a slicenums[0:1]No error. You get [10], a listnums[0]
Forgot a comma[1 2 3]SyntaxError: invalid syntax. Perhaps you forgot a comma?[1, 2, 3]
Float indexnums[len(nums) / 2]TypeError: list indices must be integers or slices, not floatnums[len(nums) // 2]
Parentheses instead of bracketsnums(0)TypeError: 'list' object is not callablenums[0]
Indexing an empty list[][0]IndexError: list index out of rangeCheck len() first
Changing a character in a stringword[0] = "J"TypeError: 'str' object does not support item assignmentBuild a new string
Step of zeronums[::0]ValueError: slice step cannot be zeroUse 1, 2, -1...

Try some at the prompt. Start by typing the first line:

  • nums = [10, 20, 30, 40]
  • nums[4]
  • nums[-1]
  • nums[1:3]
  • nums[0:1]
  • nums[2:99]
  • nums[len(nums) / 2]
  • nums(0)

Off-by-one: the most common list bug

Many of these mistakes come from counting from 1 instead of 0, or expecting the stop to be included. They do not cause errors, so the program runs and shows the wrong item. Whenever a result is "one off," check:

  • Did I use the item number (1st, 2nd) instead of the index (0, 1)?
  • Did I expect the slice's stop item to be included?

Error words to recognize: "index out of range" means too big or too negative. "must be integers" means you used a float. "not callable" means you used ( ) where you needed [ ].

  • Off-by-one mistakes give no error: they give the wrong item.
  • "One off" results: check item number vs. index, and stop exclusion.

The first four mistakes are called off-by-one errors, and they are the sneakiest because Python does not complain. You just get the wrong item. Whenever you index or slice, ask yourself: is my first index zero, and is my stop one past the last item I want?

Practice

Check yourself

Every question uses this list: nums = [4, 8, 15, 16, 23, 42]. Choose an answer, then click Check. Practice only; nothing is graded or recorded.

Write this down first:
 4   8  15  16  23  42
 0   1   2   3   4   5
-6  -5  -4  -3  -2  -1
Then: is the question an index (one value) or a slice (a list)?
  • Write the indexes above and below the list on scratch paper.

Before you answer, write the list on scratch paper with the positive indexes above it and the negative indexes below it. Then each question is just reading off your chart.

Summary

Cheat sheet

Create

nums = [10, 20, 30, 40]
empty = []
mixed = ["Ava", 15, True]
digits = list(range(5))
len(nums)   # 4

Index (one item)

nums[0]    # 10 first
nums[2]    # 30
nums[-1]   # 40 last
nums[-2]   # 30
nums[1] = 99  # change it

Positive: 0 to len - 1.
Negative: -1 to -len.

Slice (a new list)

nums[1:3]   # [20, 30]
nums[:2]    # [10, 20]
nums[2:]    # [30, 40]
nums[-2:]   # [30, 40]
nums[::2]   # [10, 30]
nums[::-1]  # reversed
nums[:]     # a copy

Remember:

  • Indexes start at 0 from the front and -1 from the back
  • Slices: start is in, stop is out; a missing start or stop means "from the beginning" or "to the end"
  • An index too big is an IndexError; a slice too big is fine
  • An index gives an item; a slice gives a list

Key words to know

list · item / element · index (positive and negative) · len() · mutable · slice · start / stop / step · sublist · copy ([:]) · IndexError · off-by-one

  • Create with [ ]. Index for one item. Slice for a sublist.
  • Know the key words: index, slice, mutable, copy, off-by-one.

Let's wrap up. Square brackets create a list. Square brackets with one number get one item: zero from the front or negative one from the back. Square brackets with a colon get a slice, a new list, where start is in and stop is out. Practice these until counting from zero feels natural.