7.3. Tuple Operations#
Common tuple operations include:
Python Expression |
Description |
Example |
|---|---|---|
|
Returns the number of items in the tuple. |
|
|
Concatenation: combines two tuples into a new tuple. |
|
|
Repetition: repeats tuple elements |
|
|
Membership: checks whether a value exists in the tuple ( |
|
|
Indexing: accesses an element using positive or negative index. |
|
|
Slicing: extracts a range of elements as a new tuple. |
|
|
Constructor: converts an iterable (list, string, set, etc.) into a tuple. |
|
import sys
from pathlib import Path
# Find project root by looking for _config.yml
current = Path.cwd()
for parent in [current, *current.parents]:
if (parent / '_config.yml').exists():
project_root = parent
break
else:
project_root = Path.cwd().parent.parent
# Add project root to path
sys.path.insert(0, str(project_root))
# Import shared teaching helpers and cell magics
from shared import thinkpython, diagram, jupyturtle, structshape
from shared.download import download
7.3.1. Tuple Operators#
The + operator concatenates tuples.
north = ('Chicago', 'Detroit', 'Minneapolis')
south = ('Atlanta', 'Dallas', 'Miami')
print(north + south)
print(type(north + south))
('Chicago', 'Detroit', 'Minneapolis', 'Atlanta', 'Dallas', 'Miami')
<class 'tuple'>
But you cannot concatenate a string and a tuple:
%%expect TypeError
north_str = 'Chicago-Detroit-Minneapolis' # a string, not a tuple
print(north_str)
print(type(north_str)) # A string is not a tuple, even if it looks like a list of items.
south = ('Atlanta', 'Dallas', 'Miami')
print(type(south))
north_str + south # TypeError: can only concatenate str to str, not tuple
Chicago-Detroit-Minneapolis
<class 'str'>
<class 'tuple'>
TypeError: can only concatenate str (not "tuple") to str
The * operator repeats a tuple a given number of times.
('Q1', 'Q2', 'Q3', 'Q4') * 2
('Q1', 'Q2', 'Q3', 'Q4', 'Q1', 'Q2', 'Q3', 'Q4')
### Exercise: Tuple Operators
# 1. Concatenate (1, 2, 3) and (4, 5, 6) into a new tuple and print it.
# 2. Repeat the tuple ('ha',) three times and print the result.
# 3. Check whether the value 7 is in (1, 3, 5, 7, 9) and print the boolean result.
### Your code starts here.
### Your code ends here.
(1, 2, 3, 4, 5, 6)
('ha', 'ha', 'ha')
True
Based on these examples, tuples can look a lot like lists.
7.3.2. Tuple Methods#
Tuples have a small set of methods. Tuples are:
Immutable (cannot be changed)
Fixed-size
Designed for safety and structure
Because you can’t modify them, they don’t have methods like:
.append()
.remove()
.sort().
The most common are count and index, which return information rather than modifying the tuple.
orders = ('PENDING', 'SHIPPED', 'PENDING', 'DELIVERED')
orders.count('PENDING'), orders.index('DELIVERED') # this returns a tuple
(2, 3)
### Exercise: Tuple Methods
# Given t = (3, 1, 4, 1, 5, 9, 2, 6, 5, 3)
# 1. Count how many times 1 appears in t.
# 2. Find the index of the first occurrence of 9.
# 3. Count how many times 5 appears.
### Your code starts here.
t = (3, 1, 4, 1, 5, 9, 2, 6, 5, 3)
### Your code ends here.
2
5
2
7.3.3. Tuple Functions#
In addition to these two methods, Python’s built-in functions (e.g., len, max, min, sum) can be used with tuples, as they work with any iterable. These functions do not modify the original tuple, but return new values or objects:
Function |
What it does |
Notes |
|---|---|---|
|
Returns the total number of items in the tuple. |
Works for any tuple. |
|
Returns the largest item in the tuple. |
Elements must be comparable. |
|
Returns the smallest item in the tuple. |
Elements must be comparable. |
|
Returns the sum of all numeric elements in the tuple. |
Elements should be numbers. |
|
Returns a new list with tuple elements in sorted order. |
Output type is |
|
Returns an iterator over tuple elements in reverse order. |
Convert with |
|
Converts another iterable (like a list or string) into a tuple. |
Useful for type conversion. |
The sorted function works with tuples, but it returns a list, which is mutable for your manipulation.
orders = ('RECEIVED', 'PENDING', 'SHIPPED', 'DELIVERED')
print(orders)
print(type(orders))
sorted(orders)
('RECEIVED', 'PENDING', 'SHIPPED', 'DELIVERED')
<class 'tuple'>
['DELIVERED', 'PENDING', 'RECEIVED', 'SHIPPED']
%%expect AttributeError
orders.sort() # this doesn't work because tuples are immutable
AttributeError: 'tuple' object has no attribute 'sort'
The reversed function also works with tuples. It returns a reversed object, which you can convert to a list or tuple.
print(reversed(orders))
tuple(reversed(orders))
<reversed object at 0x10be153f0>
('DELIVERED', 'SHIPPED', 'PENDING', 'RECEIVED')
# original object is unchanged
orders
('RECEIVED', 'PENDING', 'SHIPPED', 'DELIVERED')
### Exercise: Tuple Functions
# Given t = (4, 7, 2, 9, 1, 5)
# 1. Print the length, max, min, and sum of t.
# 2. Print a sorted version of t (as a list).
# 3. Print t in reverse order as a tuple.
### Your code starts here.
t = (4, 7, 2, 9, 1, 5)
### Your code ends here.
6 9 1 28
[1, 2, 4, 5, 7, 9]
(5, 1, 9, 2, 7, 4)
7.3.4. zip()#
Tuples are useful for pairing elements from multiple sequences and working with them together. For example, suppose a sales team has weekly targets, and we record their actual sales alongside those targets in two separate lists.
actual = [112, 98, 135, 144, 101, 129, 88]
target = [120, 120, 120, 120, 120, 120, 120]
Let’s count how many weeks the team exceeded their target.
We’ll use zip, a built-in function that combines sequences and returns a zip object, pairing elements like the teeth of a zipper.
zip(actual, target)
<zip at 0x10be36780>
zip stops when the shortest input is exhausted. If you want to keep going, use itertools.zip_longest. The parameter fillvalue can be helpful filling the empty elements.
from itertools import zip_longest
list(zip('abc', [1, 2]))
list(zip_longest('abc', [1, 2], fillvalue='-'))
[('a', 1), ('b', 2), ('c', '-')]
We can loop over the zip object to get pairwise values.
for pair in zip(actual, target):
print(pair)
(112, 120)
(98, 120)
(135, 120)
(144, 120)
(101, 120)
(129, 120)
(88, 120)
Each time through the loop, pair is a tuple of (actual_sales, target_sales).
We can unpack those values and count the weeks where actual sales exceeded the target:
weeks_above_target = 0
for sale, goal in zip(actual, target):
if sale > goal:
weeks_above_target += 1
weeks_above_target
3
The team beat their weekly target three out of seven weeks.
If you want a list of pairs, combine zip with list.
weekly_results = list(zip(actual, target))
weekly_results
[(112, 120),
(98, 120),
(135, 120),
(144, 120),
(101, 120),
(129, 120),
(88, 120)]
The result is a list of tuples, so we can get the last week’s data like this:
weekly_results[-1]
(88, 120)
If you have a list of keys and a list of values, you can use zip and dict to build a dictionary.
Here is a mapping from each letter to its position in the alphabet.
letters = 'abcdefghijklmnopqrstuvwxyz'
numbers = range(len(letters))
letter_map = dict(zip(letters, numbers))
Now we can look up a letter and get its index in the alphabet.
In this mapping, the index of 'a' is 0 and the index of 'z' is 25.
letter_map['a'], letter_map['z']
(0, 25)
7.3.5. enumerate()#
If you need to loop through elements and their indices, use Python the built-in function enumerate. The result is an enumerate object that yields pairs containing an index (starting at 0) and the corresponding element, which you then unpack into variables to use.
fruits = ['apple', 'banana', 'cherry']
for i, fruit in enumerate(fruits):
print(i, fruit)
0 apple
1 banana
2 cherry
If you want indices to start at 1 (like line numbers), pass the optional start argument.
for index, element in enumerate('abc', start=1):
print(index, element)
1 a
2 b
3 c
### Exercise: Zip
# 1. Given names = ['Alice', 'Bob', 'Carol'] and scores = [88, 95, 72],
# use zip to print each name paired with their score.
# 2. Build a dictionary from these two lists using zip and dict().
# 3. Use enumerate (starting at 1) to print each name with its rank number.
### Your code starts here.
names = ['Alice', 'Bob', 'Carol']
scores = [88, 95, 72]
### Your code ends here.
Alice 88
Bob 95
Carol 72
{'Alice': 88, 'Bob': 95, 'Carol': 72}
1 Alice
2 Bob
3 Carol
7.3.6. Comparing and Sorting#
Relational operators work with tuples and other sequences.
Tuple comparison is lexicographic: it compares the first elements, then the next, and so on until it finds a difference.
For example, we can represent a reporting period as a (year, quarter) tuple and compare two periods.
(2026, 1) < (2026, 3) # Q1 2026 is before Q3 2026
True
Once a difference is found, later elements are not considered.
(2026, 1, 999_999) < (2026, 3, 62_000) # Q1 beats Q3 in revenue, but Q3 comes later
True
This comparison behavior is useful for sorting lists of tuples or finding minimum and maximum values.
As an example, let’s find the most common letter in a word — a step often used in text analytics.
In the previous chapter, we wrote value_counts, which returns a dictionary mapping each letter to its count.
def value_counts(string):
counter = {}
for letter in string:
if letter not in counter:
counter[letter] = 1
else:
counter[letter] += 1
return counter
Here is the result for the string 'mississippi'.
counter = value_counts('mississippi')
counter
{'m': 1, 'i': 4, 's': 4, 'p': 2}
With eight distinct letters, it’s not immediately obvious which one appears most often. Sorting the items makes the answer clear.
We can get the items from counter like this:
items = counter.items()
items
dict_items([('m', 1), ('i', 4), ('s', 4), ('p', 2)])
The result is a dict_items object that behaves like a list of tuples, so we can sort it.
sorted(items)
[('i', 4), ('m', 1), ('p', 2), ('s', 4)]
### Exercise: Comparing and Sorting
# 1. Given a list of (last_name, first_name) tuples below,
# sort it lexicographically (default tuple sort) and print the result.
# 2. What does Python compare first — last name or first name?
people = [('Smith', 'John'), ('Adams', 'Zara'), ('Smith', 'Alice'), ('Adams', 'Alan')]
### Your code starts here.
### Your code ends here.
[('Adams', 'Alan'), ('Adams', 'Zara'), ('Smith', 'Alice'), ('Smith', 'John')]
7.3.7. Sorting by Value#
Sometimes you want to sort dictionary items by their values rather than their keys.
We can define a small helper that returns the second element of a (key, value) pair.
def second_element(t):
return t[1]
Then we pass that function as the optional key argument to sorted.
The key function computes a sort key for each item.
sorted_items = sorted(items, key=second_element)
sorted_items
[('m', 1), ('p', 2), ('i', 4), ('s', 4)]
The sort key determines the order. The letter with the lowest count appears first, and the highest count appears last. So we can find the most common letter like this:
sorted_items[-1]
('s', 4)
If we only want the maximum, we don’t have to sort the list.
We can use max, which also accepts a key function.
max(items, key=second_element)
('i', 4)
### Exercise: Sorting by Value
# 1. Count the letter frequencies in the word 'mississippi'.
# 2. Sort the resulting items by frequency (value), ascending.
# 3. Print the letter with the highest frequency using max() with a key function.
### Your code starts here.
### Your code ends here.
[('m', 1), ('p', 2), ('i', 4), ('s', 4)]
('i', 4)
To find the letter with the lowest count, we could use min the same way.