Python index with list

The Basics of Indexing and Slicing Python Lists

A guide for beginners, by a beginner

Joseph H

Sep 15, 2019·5 min read

Photo by Igor Miske on Unsplash

I was preparing to demonstrate indexing and slicing lists to a group of fellow Python beginners a few days ago, and I got stuck on what seemed like a couple of pretty basic use cases. So after poking around a bit to get un-stuck, I figured it was worth sharing what I learned.

Accessing the items in a list [and in other iterables like tuples and strings] is a fundamental skill for Python coders, and many Python tools follow similar conventions for indexing and slicing [e.g. numpy Arraysand pandas DataFrames]. So its worth being familiar with the ins and outs.

Definitions and Stage-Setting

Indexing means referring to an element of an iterable by its position within the iterable. Slicing means getting a subset of elements from an iterable based on their indices.

By way of analogy, I was recently summoned to jury duty, and they assigned each potential juror a number. You might say my juror number was my index. When they said, Juror number 42; please stand, I knew they were talking to me. When they said, Jurors 37 through 48, please report back at 3:30 pm, that slice of the larger group collectively sighed and went to lunch.

Lets first create a list we can play around with using a list comprehension:

my_list = [_ for _ in 'abcdefghi']
my_list
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']

Indexing

To retrieve an element of the list, we use the index operator [[]]:

my_list[0]'a'

Lists are zero indexed, so [0] returns the zero-th [i.e. the left-most] item in the list, and [1] returns the one-th item [i.e. one item to the right of the zero-th item]. Since there are 9 elements in our list [[0] through [8]], attempting to access my_list[9] throws an IndexError: list index out of range, since it is actually trying to get the tenth element, and there isnt one.

Python also allows you to index from the end of the list using a negative number, where [-1] returns the last element. This is super-useful since it means you dont have to programmatically find out the length of the iterable in order to work with elements at the end of it. The indices and reverse indices of my_list are as follows:

0 1 2 3 4 5 6 7 8

['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']

-9 -8 -7 -6 -5 -4 -3 -2 -1

Slicing

A slice is a subset of list elements. In the case of lists, a single slice will always be of contiguous elements. Slice notation takes the form

my_list[start:stop]

where start is the index of the first element to include, and stop is the index of the item to stop at without including it in the slice. So my_list[1:5] returns ['b', 'c', 'd', 'e']:

0 1 2 3 4 5 6 7 8
× × × × ×
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']

Leaving either slice boundary blank means start from [or go to] the end of the list. For example:

my_list[5:]['f', 'g', 'h', 'i']
my_list[:4]['a', 'b', 'c', 'd']

Using a negative indexer will set the start/stop bounds relative to their position from the end of the list, so my_list[-5:-2] returns ['e', 'f', 'g']:

['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
× × × × × ×
-9 -8 -7 -6 -5 -4 -3 -2 -1

Note that if you try my_list[-2:-5], youll get an empty list. This was something I got tripped up on, but heres whats happening: in order to be included in the slice, an element must be at or to the right of the start boundary AND to the left of the stop boundary. Because the -2 is already to the right of -5, the slicer stops before populating any value into the slice.

A for loop works exactly the same way; the first loop below has no output, but the second does:

for i in range[-2,-5]:
print[i]
for i in range[-5,-2]:
print[i]
-5
-4
-3

Stepping

The slicer can take an optional third argument, which sets the interval at which elements are included in the slice. So my_list[::2] returns ['a', 'c', 'e', 'g', 'i']:

0 1 2 3 4 5 6 7 8
×¹ ² ×¹ ² ×¹ ² ×¹ ²
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']

And my_list[1::2] returns ['b', 'd', 'f', 'h']:

0 1 2 3 4 5 6 7 8
× ×¹ ² ×¹ ² ×¹ ² ×¹
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']

Negative step values reverse the direction in which the slicer iterates through the original list:

my_list[::-1]['i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']

The indexed positions of list elements dont change, but the order in which the elements are returned does. The sense of the start and stop boundaries is also reversed, so the start value should be the right-most position in the slice, and the stop value should be to the left of that. So my_list[5:3:-1] gives us [f, e]:

Chủ Đề