Lists and Tuples
Ordered sequences: the mutable list and the immutable tuple, with indexing, slicing, methods and unpacking.
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)insertsxbefore indexi.
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 tox; it raisesValueErrorif there is none. -
pop()removes and returns the last item;pop(i)does the same for indexi. clear()removes all items.-
The
delstatement 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 returnsNone. -
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.