Sequences and lists

A sequence is a collection whose items are kept in a fixed order and can be reached by their position, a whole number called the index. Python has several sequence types: strings, range, and the two covered here, lists and tuples. They share the same basic operations: indexing, slicing, len(), in and looping.

A list is Python's general-purpose, changeable sequence. You write it as items between square brackets, separated by commas. A list can hold values of any type, even mixed, and it grows and shrinks as needed. The function list() builds a list from any iterable.

prices = [2.5, 4.0, 1.25]
empty = []
mixed = ["Ada", 36, True]
letters = list("abc")
numbers = list(range(1, 6))

print(prices, empty, mixed)
print(letters, numbers)
print(len(prices))
[2.5, 4.0, 1.25] [] ['Ada', 36, True]
['a', 'b', 'c'] [1, 2, 3, 4, 5]
3

Indexing and slicing

Every item has an index. The first item has index 0, the second 1, and the last len(x) - 1. Negative indexes count from the end: -1 is the last item, -2 the one before it.

A slice, x[start:stop:step], gives a new list with the items from start up to, but not including, stop. Leave out start to begin at the start, stop to go to the end, and use a negative step to walk backward. The rules are the same as for strings.

planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter"]

print(planets[0], planets[-1])
print(planets[1:3])
print(planets[:2])
print(planets[3:])
print(planets[::2])
print(planets[::-1])
Mercury Jupiter
['Venus', 'Earth']
['Mercury', 'Venus']
['Mars', 'Jupiter']
['Mercury', 'Earth', 'Jupiter']
['Jupiter', 'Mars', 'Earth', 'Venus', 'Mercury']

An index that does not exist is an error:

planets = ["Mercury", "Venus", "Earth"]
print(planets[3])
IndexError: list index out of range

Changing a list

Lists are mutable: the list object itself can be changed after it is created, without making a new one. (Strings and tuples are the opposite, immutable.) You can replace one item by assigning to an index, or a whole range of items by assigning to a slice — the new part does not even need to have the same length:

scores = [70, 85, 90]

scores[0] = 75
print(scores)

scores[1:3] = [88, 92, 95]   # replace two items with three
print(scores)

scores[1:3] = []             # remove two items
print(scores)
[75, 85, 90]
[75, 88, 92, 95]
[75, 95]

Adding items

  • append(x) adds one item at the end.
  • extend(iterable) adds every item of an iterable at the end.
  • insert(i, x) inserts x before index i.
cart = ["bread"]
cart.append("milk")
cart.extend(["eggs", "cheese"])
cart.insert(0, "apples")
print(cart)

a = [1, 2]
a.append([3, 4])   # one item: a list
b = [1, 2]
b.extend([3, 4])   # two items
print(a, len(a))
print(b, len(b))
['apples', 'bread', 'milk', 'eggs', 'cheese']
[1, 2, [3, 4]] 3
[1, 2, 3, 4] 4
nums = [1, 2, 3]
nums.insert(-1, 99)
nums.insert(100, 7)
print(nums)
[1, 2, 99, 3, 7]

Removing items

  • remove(x) removes the first item equal to x; it raises ValueError if there is none.
  • pop() removes and returns the last item; pop(i) does the same for index i.
  • clear() removes all items.
  • The del statement removes an item or a slice by position: del x[0], del x[1:3].
queue = ["Ada", "Grace", "Linus", "Grace"]

queue.remove("Grace")      # first match only
print(queue)

last = queue.pop()
print(last, queue)

first = queue.pop(0)
print(first, queue)

del queue[0]
print(queue)
['Ada', 'Linus', 'Grace']
Grace ['Ada', 'Linus']
Ada ['Linus']
[]

Finding and counting items

index(x) returns the position of the first item equal to x (and raises ValueError if it is missing), count(x) counts how often it occurs, and the in operator tells whether it occurs at all.

votes = ["yes", "no", "yes", "yes"]

print(votes.count("yes"))
print(votes.index("no"))
print("maybe" in votes)
3
1
False

Sorting and reversing

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

  • The method list.sort() sorts the list in place. The original order is gone, and the method returns None.
  • The built-in function sorted() leaves its argument alone and returns a new sorted list. It accepts any iterable, including tuples and strings.

Both take reverse=True for descending order and key= for a function that computes what to sort by. reverse() reverses a list in place without sorting.

temps = [21, 18, 25, 19]

ordered = sorted(temps)
print(ordered, temps)

temps.sort(reverse=True)
print(temps)

result = temps.sort()
print(result)

fruit = ["banana", "Cherry", "apple"]
print(sorted(fruit))                  # uppercase sorts before lowercase
print(sorted(fruit, key=str.lower))   # ignore case

steps = [1, 2, 3]
steps.reverse()
print(steps)
[18, 19, 21, 25] [21, 18, 25, 19]
[25, 21, 19, 18]
None
['Cherry', 'apple', 'banana']
['apple', 'banana', 'Cherry']
[3, 2, 1]

Working with lists

Several built-in functions and operators work on any list: len() counts the items, min() and max() find the smallest and largest, sum() adds numbers up, in and not in test membership, + joins two lists into a new one and * repeats one.

prices = [4.5, 2.25, 8.0]

print(len(prices), min(prices), max(prices), sum(prices))
print(8.0 in prices, 3 not in prices)
print([1, 2] + [3], [0] * 3)
3 2.25 8.0 14.75
True True
[1, 2, 3] [0, 0, 0]

To visit every item, use a for loop; to build a new list from an existing one, a list comprehension is often the shortest way.

Nested lists

A list can contain other lists. A list of rows, each row a list of the same length, is the usual way to represent a table or matrix. The first index picks the row, the second the column: grid[1][2] is row 1, column 2.

grid = [
    [1, 2, 3],
    [4, 5, 6],
]

print(grid[1][2])
grid[0][0] = 9

for row in grid:
    print(row)

print(len(grid), len(grid[0]))   # rows, columns
6
[9, 2, 3]
[4, 5, 6]
2 3

Aliasing and copying

A variable does not contain a list; it refers to one. The assignment alias = original copies only that reference, so both names now refer to the same list. This is called aliasing. A change made through one name is visible through the other, because there is only one list. The is operator shows whether two names refer to the same object.

To get an independent list, make a copy. There are three common ways, all equivalent: original.copy(), the full slice original[:], and list(original).

original = ["tea", "coffee"]

alias = original
alias.append("juice")
print(original)
print(alias is original)

duplicate = original.copy()      # or original[:] or list(original)
duplicate.append("water")
print(original, duplicate)
print(duplicate is original)
['tea', 'coffee', 'juice']
True
['tea', 'coffee', 'juice'] ['tea', 'coffee', 'juice', 'water']
False

Shallow and deep copies

All three ways make a shallow copy: a new outer list whose items are the same objects as in the original. For numbers and strings that makes no difference, because they cannot change. For nested lists it does: the inner lists are shared. If you need a fully independent copy of a nested structure, use copy.deepcopy() from the standard library, which copies the inner objects too.

import copy

teams = [["Ada", "Grace"], ["Linus"]]
shallow = teams.copy()
deep = copy.deepcopy(teams)

teams[1].append("Guido")
print(shallow)
print(deep)
[['Ada', 'Grace'], ['Linus', 'Guido']]
[['Ada', 'Grace'], ['Linus']]
board = [[0] * 3] * 3
board[0][0] = 1
print(board)

board = [[0] * 3 for _ in range(3)]
board[0][0] = 1
print(board)
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]

Tuples

A tuple is an ordered sequence, like a list, but immutable: once it is created, you cannot add, remove or replace its items. You write it with commas, usually between parentheses. It is the comma that makes the tuple, not the parentheses — except the empty tuple, which is written (). That is why a tuple with one item needs a trailing comma.

point = (3, 4)
rgb = 255, 128, 0          # parentheses are optional
print(point[0], rgb)
print(len(rgb), type(rgb))

single = ("Oslo",)         # one-item tuple
not_a_tuple = ("Oslo")     # just a string in parentheses
print(type(single), type(not_a_tuple))

empty = ()
print(len(empty))
3 (255, 128, 0)
3 <class 'tuple'>
<class 'tuple'> <class 'str'>
0

Indexing, slicing, len(), in, +, * and looping work exactly as for lists, and a tuple has the two methods that do not change anything: count() and index(). Anything that would modify it fails:

point = (3, 4)
point[0] = 5
TypeError: 'tuple' object does not support item assignment

Packing and unpacking

Writing record = "Ada", 36, "London" packs three values into one tuple. The reverse is unpacking: assigning a sequence to several variables at once, one per item. The number of variables must match the number of items exactly, or Python raises a ValueError. Unpacking works with any iterable — lists and strings as well as tuples.

A starred name, *rest, collects all items that the other names do not take, always as a list (possibly empty). Only one starred name is allowed per assignment.

record = ("Ada", 36, "London")
name, age, city = record
print(name, age, city)

first, *rest = [1, 2, 3, 4]
print(first, rest)

*init, last = "abc"
print(init, last)

head, *middle, tail = (10, 20, 30, 40, 50)
print(middle)
Ada 36 London
1 [2, 3, 4]
['a', 'b'] c
[20, 30, 40]

Swapping values

Packing and unpacking together give Python its well-known swap. The right-hand side is evaluated first and packed into a tuple; only then is it unpacked into the names on the left. No temporary variable is needed.

a = "left"
b = "right"
a, b = b, a
print(a, b)
right left

Lists or tuples?

Both keep items in order, so the choice is about meaning and safety:

  • Use a list for a collection that grows, shrinks or gets reordered — usually many items of the same kind: a shopping cart, a list of log lines, measurements.
  • Use a tuple for a fixed group of values that belong together, where each position has its own meaning: a coordinate (x, y), an RGB color, a database row, or several values returned from a function.

Because a tuple cannot change, you can pass it around without fear that some other code modifies it. And a tuple whose items are all hashable is itself hashable, so, unlike a list, it can be used as a dictionary key or stored in a set — see Dictionaries and Sets.