Tuples are another one of Python’s built-in collection objects. Since tuples implement the immutable sequence type, they boast much of the same functionality as lists, with the main difference being that tuples are immutable.
You can declare tuples like so
# literals belchers = ('Bob Belcher', 'Linda Belcher') # constructor pestos = tuple('Jimmy Pesto', 'Andy Pesto')
You can also convert a list to a tuple using this code
# Make a list belcher_list = ['Bob Belcher', 'Linda Belcher'] # Turn list to a tuple belcher_tuple = tuple(belcher_list) # Back to a list now new_belcher_list = list(belcher_tuple)
Tuples generally have the same methods as lists, but you are prohibited from doing anything that modifies the tuple
- No reassignments
- No adding elements
- No removing elements
- No sorting or shuffling
However, you are free to modify objects stored in a tuple and you are free to use any of the read methods on a tuple. So for example, you can still access items in a tuple by using the  operator, and tuples support iteration allowing the to be used in a for loop.
Need for Tuples
Immutable is a fancy programming word that means an object is read-only. Unlike a list, tuples forbid operations that modify the tuple. That means you are not free to add, remove, or sort tuples (note that you can modify the objects stored in a tuple just not the tuple itself). Tuples are useful in programming because they provide a safe guard against inadvertant modification of a sequence.
For exampe, let’s suppose that we have a database table that looks like the one below.
- ID (PK)
- First Name
- Last Name
- Job Title
When we run database queries against this table, we most likely aren’t going to want to change the order of the result set’s columns. However, a list would allow use to do that exact sort of thing. We could certainly program defensively to make sure our code doesn’t modify the order the columns, but even so, it’s easy to write code like the below example.
def nested_func(result_set): # Modify the result_set # in a nested function result_set.reverse() def func(result_set): # Do some sort of work nested_func(result_set) if __name__ == '__main__': # Store DB row in result_set # We don't want the order to change result_set = [1, 'Bob', 'Belcher', 'Owner'] print(result_set) func(result_set) # Did our order change? print(result_set)
Remember that our intention was to maintain the order of result_set. We call func() which in turns calls nested_func. Here is the result of this program when executed.
[1, 'Bob', 'Belcher', 'Owner'] ['Owner', 'Belcher', 'Bob', 1]
This is a simple enough of a programming error to make on it’s own, but when a developer works with larger programs with multiple modules, it becomes an even easier error to make and worse, an even harder program to debug.
However, if we change result_set to a tuple rather than a list, the Python runtime will help us out and catch the error for us!
def nested_func(result_set): # Modify the result_set # in a nested function result_set.reverse() def func(result_set): # Do some sort of work nested_func(result_set) if __name__ == '__main__': # Store DB row in result_set # Notice that it's a tuple now!!! result_set = (1, 'Bob', 'Belcher', 'Owner') print(result_set) func(result_set) # Did our order change? print(result_set)
When executed, the program displays this output to the console instead…
AttributeError: 'tuple' object has no attribute 'reverse'
In this version of the program, nested_func attempts to call reverse() on a tuple object. Tuples do not have a reverse method, so the program crashes with an AttributeError along with the output of the stack so that we can track down where the error occurred in the program. In this way, the tuple protected us from a difficult to find bug that we would have had we used a list object.