Lesson 6, Bit 4: Tuples
Tuples
Tuples are used to hold together multiple objects. Think of them as similar to lists, but without the extensive functionality that the list class gives you. One major feature of tuples is that they are immutable like strings i.e. you cannot modify tuples.
Tuples are defined by specifying items separated by commas within an optional pair of parentheses.
Tuples are usually used in cases where a statement or a user-defined function can safely assume that the collection of values i.e. the tuple of values used will not change.
Fun fact: The word "tuple" comes from the names given to sequences of numbers of varying lengths: single, double, triple, quadruple, quituple, sextuple, septuple, etc.
Tuples are Immutable
A tuple is a sequence of values much like a list. The values stored in a tuple can be any type, and they are indexed by integers. The important difference is that tuples are immutable. Tuples are also comparable and hashable so we can sort lists of them and use tuples as key values in Python dictionaries.
Syntactically, a tuple is a comma-separated list of values:
t = 'a',
'b', 'c', 'd', 'e'Although it is not necessary, it is common and a best practice to enclose tuples in parentheses to help us quickly identify tuples when we look at Python code:
t = ('a', 'b', 'c', 'd', 'e')To create a tuple with a single element, you have to include the final comma:
| Code | Result |
|---|---|
t1 = ('a',) |
<type 'tuple'> |
Without the comma Python treats ('a') as an expression with a
string in parentheses that evaluates to a string:
| Code | Result |
|---|---|
t2 = ('a') |
<type 'str'> |
Another way to construct a tuple is the built-in function tuple. With no argument, it creates an empty tuple:
| Code | Output |
|---|---|
t = tuple() |
() |
If the argument is a sequence (string, list, or tuple), the result of the call to tuple is a tuple with the elements of the sequence:
| Code | Output |
|---|---|
t = tuple('lupins') |
('l', 'u', 'p', 'i', 'n', 's') |
Because tuple is the name of a constructor, you should avoid
using it as a variable name.
Most list operators also work on tuples. The bracket operator indexes an element:
| Code | Output |
|---|---|
t = ('a', 'b', 'c', 'd', 'e') |
a |
And the slice operator selects a range of elements.
| Code | Output |
|---|---|
t = ('a', 'b', 'c', 'd', 'e') |
('b', 'c') |
But if you try to modify one of the elements of the tuple, you get an error:
| Code | Output |
|---|---|
t = ('a', 'b', 'c', 'd', 'e') |
TypeError: 'tuple' object does not support item
assignment |
You can't modify the elements of a tuple, but you can replace one tuple with another:
| Code | Output |
|---|---|
t = ('a', 'b', 'c', 'd', 'e') |
('A', 'b', 'c', 'd', 'e') |
Comparing Tuples
The comparison operators work with tuples and other sequences. Python starts by comparing the first element from each sequence. If they are equal, it goes on to the next element, and so on, until it finds elements that differ. Subsequent elements are not considered (even if they are really big).
| Code | Result |
|---|---|
(0, 1, 2) < (0, 3, 4) |
True |
(0, 1, 2000000) < (0, 3, 4) |
True |
The sort function works the same way. It sorts primarily by
first element, but in the case of a tie, it sorts by second element, and so
on.
This feature lends itself to a pattern called DSU for:
Decorate: a sequence by building a list of tuples with one or more sort keys preceding the elements from the sequence,
Sort: the list of tuples using the Python built-in sort, and
Undecorate: by extracting the sorted elements of the sequence.
For example, suppose you have a list of words and you want to sort them from longest to shortest:
txt = 'Tis but a flesh wound'
words =
txt.split()
t = list()
for word in words:
t.append((len(word), word))
t.sort(reverse=True)
res = list()
for length, word in
t:
res.append(word)
print(res)
| Code | Output | Notes |
|---|---|---|
txt = 'Tis but a flesh wound' |
None |
We create the string |
words = txt.split() |
None |
The |
t = list() |
None |
We create an empty list called |
for word in words: |
None |
Iterate through the list words. Append to the list |
t.sort(reverse=True) |
None |
|
res = list() |
None |
We create an empty list called |
for length, word in t: |
None |
The second loop traverses the list of tuples and builds a list of words in descending order of length. The four-character words are sorted in reverse alphabetical order, so "what" appears before "soft" in the following list. |
print(res) |
['wound', 'flesh', 'but', 'Tis', 'a'] |
We display the resulting list. |
Tuple Assignment
One of the unique syntactic features of the Python language is the ability to have a tuple on the left side of an assignment statement. This allows you to assign more than one variable at a time when the left side is a sequence.
In this example we have a two-element list (which is a sequence) and assign
the first and second elements of the sequence to the variables x
and y in a single statement.
m = ['have', 'fun']
x, y = m
| Code | Output |
|---|---|
print(x) |
have |
print(y) |
fun |
It is not magic, Python roughly translates the tuple assignment syntax to be the following:
m = ['have', 'fun']
x = m[0]
y
= m[1]
| Code | Output |
|---|---|
print(x) |
have |
print(y) |
fun |
It should be noted that Python does not translate the syntax literally. For example, if you try this with a dictionary, it will not work as you might expect.
Stylistically when we use a tuple on the left side of the assignment statement, we omit the parentheses, but the following is an equally valid syntax:
m = ['have', 'fun']
(x, y) = m
| Code | Output |
|---|---|
print(x) |
have |
print(y) |
fun |
A particularly clever application of tuple assignment allows us to swap the values of two variables in a single statement:
a, b = b, a
Both sides of this statement are tuples, but the left side is a tuple of variables; the right side is a tuple of expressions. Each value on the right side is assigned to its respective variable on the left side. All the expressions on the right side are evaluated before any of the assignments.
The number of variables on the left and the number of values on the right must be the same:
| Code | Result |
|---|---|
a, b = 1, 2, 3 |
ValueError: too many values to unpack (expected
2) |
More generally, the right side can be any kind of sequence (string, list, or tuple). For example, to split an email address into a user name and a domain, you could write:
addr = 'monty@python.org'
uname, domain =
addr.split('@')
The return value from split is a list with two elements; the
first element is assigned to uname, the second to
domain.
| Code | Output |
|---|---|
print(uname) |
monty |
print(domain) |
python.org |
