Previous Up Next

Chapter 12  Tuples

12.1  Tuples are immutable

A tuple is a sequence of values. The values can be any type, and they are indexed by integers, so in that respect tuples are a lot like lists. The important difference is that tuples are immutable.

Syntactically, a tuple is a comma-separated list of values:

>>> tuple = 'a', 'b', 'c', 'd', 'e'

Although it is not necessary, it is common to enclose tuples in parentheses:

>>> t = ('a', 'b', 'c', 'd', 'e')

To create a tuple with a single element, you have to include the final comma:

>>> t1 = ('a',)
>>> type(t1)
<type 'tuple'>

Without the comma, Python treats ('a') as a string in parentheses:

>>> t2 = ('a')
>>> type(t2)
<type 'str'>

Another way to create a tuple is the function tuple. With no argument, it creates an empty tuple:

>>> t = tuple()
>>> print t
()

If the argument is a sequence (string, list or tuple), the result is a tuple with the elements of the sequence:

>>> t = tuple('lupins')
>>> print t
('l', 'u', 'p', 'i', 'n', 's')

Most list operators also work on tuples. The bracket operator indexes an element:

>>> t = ('a', 'b', 'c', 'd', 'e')
>>> print t[0]
'a'

And the slice operator selects a range of elements.

>>> print t[1:3]
('b', 'c')

But if you try to modify one of the elements of the tuple, you get an error:

>>> t[0] = 'A'
TypeError: object doesn't support item assignment

You can't modify the elements of a tuple, but you can replace one tuple with another:

>>> t = ('A',) + t[1:]
>>> print t
('A', 'b', 'c', 'd', 'e')

12.2  Tuple assignment

It is often useful to swap the values of two variables. With conventional assignments, you have to use a temporary variable. For example, to swap a and b:

>>> temp = a
>>> a = b
>>> b = temp

This solution is cumbersome; tuple assignment is more elegant:

>>> a, b = b, a

The left side is a tuple of variables; the right side is a tuple of expressions. Each value is assigned to its respective variable. 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 have to be the same:

>>> a, b = 1, 2, 3
ValueError: too many values to unpack

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.

>>> print uname
monty
>>> print domain
python.org

12.3  Tuples as return values

Strictly speaking, a function can only return one value, but if the value is a tuple, the effect is the same as returning multiple values. For example, if you want to divide two integers and compute the quotient and remainder, it is inefficient to compute x/y and then x%y. It is better to compute them both at the same time.

The built-in function divmod takes two arguments and returns a tuple of two values, the quotient and remainder. You can store the result as a tuple:

>>> t = divmod(7, 3)
>>> print t
(2, 1)

Or use tuple assignment to store the elements separately:

>>> quot, rem = divmod(7, 3)
>>> print quot
2
>>> print rem
1

Here is an example of a function that returns a tuple:

def min_max(t):
    return min(t), max(t)

max and min are built-in functions that find the largest and smallest elements of a sequence. max_min computes both and returns a tuple of two values.

12.4  Lists and tuples

zip is a built-in function that takes two or more sequences and “zips” them into a list of tuples, where each tuple contains one element from each sequence.

This example zips a string and a list:

>>> s = 'abc'
>>> t = [0, 1, 2]
>>> zip(s, t)
[('a', 0), ('b', 1), ('c', 2)]

The result is a list of tuples where each tuple contains a character from the string and the corresponding element from the list.

If the sequences are not the same length, the result gets the length of the shorter one.

>>> zip('Anne', 'Elk')
[('A', 'E'), ('n', 'l'), ('n', 'k')]

You can use tuple assignment to traverse a list of tuples:

t = [('a', 0), ('b', 1), ('c', 2)]
for letter, number in t:
    print number, letter

Each time through the loop, Python selects the next tuple in the list and assigns the elements to letter and number. The output of this loop is:

0 a
1 b
2 c

If you combine zip, for and tuple assignment, you get a standard idiom for traversing two (or more) sequences at the same time. For example, has_match takes two sequences, t1 and t2, and returns True if there is an index i such that t1[i] == t2[i]:

def has_match(t1, t2):
    for x, y in zip(t1, t2):
        if x == y:
            return True
    return False

If you need to traverse the elements of a sequence and their indices, you can use the built-in function enumerate:

for index, element in enumerate('abc'):
    print index, element

The output of this loop is:

0 a
1 b
2 c

Again.

12.5  Dictionaries and tuples

Dictionaries have a method called items that returns a list of tuples, where each tuple is a key-value pair.

>>> d = {'a':0, 'b':1, 'c':2}
>>> t = d.items()
>>> print t
[('a', 0), ('c', 2), ('b', 1)]

As you should expect from a dictionary, the items are in no particular order.

Conversely, you can use a list of tuples to initialize a new dictionary:

>>> t = [('a', 0), ('c', 2), ('b', 1)]
>>> d = dict(t)
>>> print d
{'a': 0, 'c': 2, 'b': 1}

Combining this feature with zip yields a concise way to create a dictionary:

>>> d = dict(zip('abc', range(3)))
>>> print d
{'a': 0, 'c': 2, 'b': 1}

The dictionary method update also takes a list of tuples and adds them, as key-value pairs, to an existing dictionary.

Combining items, tuple assignment and for, you get the idiom for traversing the keys and values of a dictionary:

for key, value in d.items():
    print value, key

The output of this loop is:

0 a
2 c
1 b

Again.

It is common to use tuples as keys in dictionaries (primarily because you can't use lists). For example, a telephone directory might map from last-name, first-name pairs to telephone numbers. Assuming that we have defined last, first and number, we could write:

directory[last,first] = number

The expression in brackets is a tuple. We could use tuple assignment to traverse this dictionary.

for last, first in directory:
    print first, last, directory[last,first]

This loop traverses the keys in directory, which are tuples. It assigns the elements of each tuple to last and first, then prints the name and corresponding telephone number.

There are two ways to represent tuples in a state diagram. The more detailed version shows the indices and elements just as they appear in a list. For example, the tuple ('Cleese', 'John') would appear:

But in a larger diagram you might want to leave out the details. For example, a diagram of the telephone directory might appear:

Here the tuples are shown using Python syntax as a graphical shorthand.

The telephone number in the diagram is the complaints line for the BBC, so please don't dial it.

12.6  Sorting 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 elements, and so on, until it finds elements that differ. Subsequent elements are not considered (even if they are really big).

>>> (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. Here is an example that sorts and prints the key-value pairs of a dictionary:

>>> d = {'a': 0, 'c': 2, 'b': 1}
>>> t = d.items()
>>> t.sort()
>>> print t
[('a', 0), ('b', 1), ('c', 2)]

To sort by value (rather than key), you can build a list of value-key pairs. One way to do that is to traverse the dictionary items and append tuples onto a list:

def value_key_pairs(d):
    res = []
    for key, value in d.items():
        res.append((value, key))
    return res

The argument for append has two sets of parentheses: one because its an argument and the other because it is a tuple.

Exercise 1   Draw a diagram that shows the final state of value_key_pairs with d = {'a': 0, 'c': 2, 'b': 1}.

12.7  Sequences of sequences

I have focused on lists of tuples, but almost all of the examples in this chapter also work with lists of lists, tuples of tuples, and tuples of lists. To avoid enumerating the possible combinations, it is sometimes easier to talk about sequences of sequences.

In many contexts, the different kinds of sequences (strings, lists and tuples) can be used interchangeably. So how and why do you choose one over the others.

To start with the obvious, strings are more limited than other sequences because the elements have to be characters. They are also immutable. If you need the ability to change the characters in a string (as opposed to creating a new string), you might want to use a list of characters instead.

Lists are more common than tuples, mostly because they are mutable. But there are a few cases where you might prefer tuples:

Because tuples are immutable, they don't provide methods like sort and reverse, which modify existing lists. But Python provides the built-in functions sorted and reversed, which take any sequence as a parameter are return a new list with the same elements in a different order.

12.8  Debugging

Using immutable types to eliminate aliasing.

The best way to avoid a bug is to make it impossible.

12.9  Glossary

tuple:
An immutable sequence of elements.
tuple assignment:
An assignment with a sequence on the right side and a tuple of variables on the left. The right side is evaluated and then its elements are assigned to the variables on the left.

12.10  Exercises

Exercise 2   Write a function called most_frequent that takes a string and prints the 3 most common letters in the string.
Exercise 3  

Write a program that reads a word list from a file (see Section 9.1) and prints all the sets of words that are anagrams.

Here is an example of what the output might look like:

['deltas', 'desalt', 'lasted', 'salted', 'slated', 'staled']
['retainers', 'ternaries']
['generating', 'greatening']
['resmelts', 'smelters', 'termless']

Hint: you might want to build a dictionary that maps from a set of letters to a list of words that can be spelled with those letters. The question is, how can you represent the set of letters in a way that can be used as a key?

Exercise 4   Modify the previous program so that it prints the largest set of anagrams first, followed by the second largest set, and so on.

Previous Up Next