DataLens

Lists, indexing, and slicing

Store many values in one ordered list, read items by position, take slices, and change lists with their methods.

Level: Foundations · 12 min read

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

The idea

So far each variable has held one value. But data comes in groups: all the products in a category, every order today, the last seven days of revenue. A list holds many values, in order, under one name.

cities = ["Jakarta", "Bandung", "Surabaya"]
daily_orders = [12, 9, 15, 7, 11]
print(cities)
print(len(daily_orders))
['Jakarta', 'Bandung', 'Surabaya']
5

A list is written with square brackets, with items separated by commas. len() tells you how many items it has. A list can hold any type, even a mix, but in practice a good list holds one kind of thing: all prices, or all names.

Analogy

A list is a numbered row of lockers. Each locker has a number (starting at 0) and holds one item. You can open a locker by its number, add lockers at the end, or take one out.

Indexing: getting one item

Each item has a position, its index, starting from 0, exactly like the characters of a string:

cities = ["Jakarta", "Bandung", "Surabaya", "Medan"]
print(cities[0])
print(cities[2])
print(cities[-1])
print(cities[-2])
Jakarta
Surabaya
Medan
Surabaya

Negative indexes count from the end: -1 is the last item, -2 the second-to-last. Ask for an index that doesn't exist and Python stops:

print(cities[4])
IndexError: list index out of range

A list of 4 items has indexes 0 to 3. Off-by-one mistakes like this are the classic list bug.

Slicing: getting a range

A slice gives you a new list with part of the original, using the same start:stop rule you learned for strings: from start up to, but not including, stop.

revenue = [1693, 1957, 1517, 4227, 2552, 1680]   # thousands of Rupiah, Jan-Jun
print(revenue[0:3])    # first quarter
print(revenue[3:])     # second quarter
print(revenue[-2:])    # last two months
print(revenue[::2])    # every second month
[1693, 1957, 1517]
[4227, 2552, 1680]
[2552, 1680]
[1693, 1517, 2552]

The optional third number is the step: [::2] takes every second item. [::-1] is a handy idiom that gives a reversed copy.

Lists can change

Here's a big difference from strings. A list of today's stock levels isn't something you build once and leave alone: items get sold, deliveries come in, a typo needs fixing. So unlike strings, lists are mutable, which means you can change them in place without making a new list. To replace one item, assign to its index:

stock = [24, 12, 80]
stock[1] = 15          # replace an item
print(stock)
[24, 15, 80]

Adding and removing items

Lists come with methods for adding and removing. Unlike string methods, most of these change the list itself and return None:

basket = ["Kopi Susu", "Roti Bakar"]
basket.append("Teh Tarik")          # add to the end
basket.insert(0, "Air Mineral")     # add at a position
print(basket)

basket.remove("Roti Bakar")         # remove the first matching value
last = basket.pop()                 # remove and return the last item
print(basket, last)
['Air Mineral', 'Kopi Susu', 'Roti Bakar', 'Teh Tarik']
['Air Mineral', 'Kopi Susu'] Teh Tarik
MethodWhat it does
.append(x)adds x to the end
.insert(i, x)inserts x at index i
.extend(other)adds every item from another list
.remove(x)removes the first item equal to x (error if missing)
.pop() / .pop(i)removes and returns the last item, or the one at i
Watch out

Because .append() changes the list and returns None, writing basket = basket.append("Teh") replaces your list with None. Just call basket.append("Teh") on its own line. This is the opposite of string methods, where you must keep the result.

Searching

With a list of customer ratings, you'll want to ask things like "did anyone give us a 4.8?", "how many did?" and "where's the first one?". There's a tool for each question:

ratings = [4.6, 4.5, 4.8, 4.3, 4.8]
print(4.8 in ratings)
print(ratings.count(4.8))
print(ratings.index(4.8))
True
2
2

in asks whether a value is in the list, .count() counts it, and .index() gives the position of its first appearance.

Sorting

There are two ways to sort, and the difference matters:

prices = [95000, 65000, 110000, 55000]
cheapest_first = sorted(prices)
print(cheapest_first)
print(prices)

prices.sort(reverse=True)
print(prices)
[55000, 65000, 95000, 110000]
[95000, 65000, 110000, 55000]
[110000, 95000, 65000, 55000]

Use sorted() when you still need the original order, and .sort() when you don't.

Handy built-ins for number lists

A list of numbers, like orders per day, is usually there so you can summarise it: the total for the week, the slowest day, the busiest day, the average. Python's built-in functions take a whole list and give you those answers directly, with no loop needed:

daily_orders = [12, 9, 15, 7, 11]
print(sum(daily_orders), min(daily_orders), max(daily_orders))
print(sum(daily_orders) / len(daily_orders))
54 7 15
10.8

sum(), min() and max() work on any list of numbers, and sum() / len() gives the average.

Lists of lists

An item can itself be a list, which gives you a simple table: a list of rows.

orders = [
    ["Ayu", "Jakarta", 285000],
    ["Budi", "Bandung", 960000],
]
print(orders[1])
print(orders[1][2])
['Budi', 'Bandung', 960000]
960000

orders[1] is the second row, and orders[1][2] is the third item of that row. This works, but as soon as you need named fields, a dictionary (coming soon) or a pandas DataFrame is much clearer than remembering that index 2 means "total".

Copying a list

Assigning a list to a new name does not copy it. Both names point to the same list:

original = [1, 2, 3]
alias = original
alias.append(4)
print(original)

copy = original.copy()
copy.append(5)
print(original, copy)
[1, 2, 3, 4]
[1, 2, 3, 4] [1, 2, 3, 4, 5]

When you want an independent list, use .copy() (or a full slice, original[:]).

Why this matters for clean code

Name lists in the plural (cities, prices, orders) so cities[0] reads as "the first city". Keep one kind of thing per list. And know which operations change a list and which make a new one: most list bugs come from changing a list you thought was a copy, or from storing the None that an in-place method returned.

Recap

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

Next: Tuples and when to use them →