6.3. Tuples#
Some values belong together and should not change: a location’s latitude and longitude, a date’s year, month, and day, or one row of a sales report. A tuple stores values like these as a single, fixed sequence. Section 6.2 showed that a list can change through any name that refers to it; a tuple cannot change at all, which makes it safe to share and a natural way to represent records.
This section covers what tuples are, how to create and access them, what immutability does and does not guarantee, how to unpack tuples, the operations tuples support, how functions use tuples, and how to work with tuples as records.
6.3.1. What Tuples Are#
There are two common pronunciations of “tuple”. Some people say “tuh-ple” (rhymes with “supple”), and many programmers say “too-ple” (rhymes with “quadruple”).
A tuple is a sequence of values.
The values can be any type.
The values in a tuple are indexed by integers, so tuples feel a lot like lists.
When a function returns multiple values, it returns a tuple.
Because tuples can’t change, they can be used in places that require fixed values, such as dictionary keys (Chapter 7).
For example, a latitude and longitude pair is a natural tuple:
location = (42.3601, -71.0589)
Use a tuple when the values belong together and should not change; use a list when you need to add, remove, or change items. The table compares the two:
Feature |
Tuple |
List |
|---|---|---|
Syntax |
|
|
Mutable |
No |
Yes |
Methods |
2 ( |
Many |
Use case |
Fixed data, records |
Collections that change |
6.3.2. Creating and Accessing Tuples#
Tuples in Python can be created in several ways, primarily:
Using tuple literals: through the use of commas and optional parentheses
by using the built-in tuple() constructor
How to create |
Method |
Example |
Result |
|---|---|---|---|
Use parentheses with comma-separated values |
Parentheses |
|
|
Write comma-separated values without parentheses |
Tuple packing |
|
|
Use empty parentheses |
Empty tuple |
|
|
Add a trailing comma for one value |
Single-item tuple |
|
|
Use the tuple constructor on an iterable |
Constructor |
|
|
6.3.2.1. Tuple Literals#
To create a tuple, you can write a comma-separated list of values.
status = 'NEW', 'PROCESSING', 'SHIPPED', 'DELIVERED', 'CLOSED'
type(status)
tuple
Parentheses are optional, but it is common to use them with tuples.
t1 = ('NEW', 'PROCESSING', 'SHIPPED', 'DELIVERED', 'CLOSED')
print(t1)
print(type(t1))
t2 = 'NEW', 'PROCESSING', 'SHIPPED', 'DELIVERED', 'CLOSED' # tuple packing
print(t2)
print(type(t2))
('NEW', 'PROCESSING', 'SHIPPED', 'DELIVERED', 'CLOSED')
<class 'tuple'>
('NEW', 'PROCESSING', 'SHIPPED', 'DELIVERED', 'CLOSED')
<class 'tuple'>
6.3.2.2. Single-Element Tuples#
To make a one-element tuple, include a trailing comma. Parentheses are optional.
t1 = ('URGENT',)
print(type(t1))
print(t1[0])
t2 = 'URGENT', # tuple packing with one element
print(type(t2))
print(t2[0])
<class 'tuple'>
URGENT
<class 'tuple'>
URGENT
However, a single value inside parentheses is not a tuple without a comma.
t2 = ('URGENT')
type(t2)
str
6.3.2.3. tuple() Constructor#
Another way to create a tuple is with the built-in function/type constructor tuple.
If the argument is any iterable, such as a list, a string, or a range, tuple returns a tuple containing its elements. The list constructor works the same way in reverse, turning a tuple into a list.
t = tuple(['Laptop', 'Mouse', 'Keyboard', 'Monitor'])
print(t)
print(type(t))
l = list(('Laptop', 'Mouse', 'Keyboard', 'Monitor'))
print(l)
print(type(l))
('Laptop', 'Mouse', 'Keyboard', 'Monitor')
<class 'tuple'>
['Laptop', 'Mouse', 'Keyboard', 'Monitor']
<class 'list'>
With no argument, tuple() returns an empty tuple. Empty parentheses do the same:
t = tuple()
print(t, type(t))
empty = ()
print(empty, type(empty))
() <class 'tuple'>
() <class 'tuple'>
6.3.2.4. Nested Tuples#
A nested tuple is a tuple that contains one or more tuples as elements. This is useful for representing multi-dimensional or grouped data, such as a matrix or a list of coordinate pairs. You access elements using chained indexing: the first index selects the inner tuple, and the second index selects an element within it.
# A tuple containing other tuples
matrix = ((1, 2, 3), (4, 5, 6), (7, 8, 9))
# Access the second row
print(matrix[1]) # (4, 5, 6)
# Access the third element of the second row
print(matrix[1][2]) # 6
# Tuple of coordinate pairs
points = ((0, 0), (1, 2), (3, 4))
for x, y in points:
print(f'x={x}, y={y}')
(4, 5, 6)
6
x=0, y=0
x=1, y=2
x=3, y=4
### Exercise: Creating Tuples *(basic)*
# 1. Create a tuple named `colors` containing "red", "green", "blue" using parentheses.
# 2. Create a single-element tuple named `one` containing the integer 42.
# 3. Create a tuple named `letters` by passing the string "hello" to the tuple() constructor.
# 4. Print each tuple and its type.
### Your code starts here.
### Your code ends here.
('red', 'green', 'blue') <class 'tuple'>
(42,) <class 'tuple'>
('h', 'e', 'l', 'l', 'o') <class 'tuple'>
6.3.2.5. Indexing and Slicing#
Tuples support indexing and slicing the same way lists do.
t = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri')
print(t)
t[0]
('Mon', 'Tue', 'Wed', 'Thu', 'Fri')
'Mon'
The slice operator selects a range of elements.
t[1:3]
('Tue', 'Wed')
### Exercise: Indexing and Slicing *(basic)*
# Given the tuple: t = ('a', 'b', 'c', 'd', 'e')
# 1. Print the first and last elements using indexing.
# 2. Print the middle three elements using slicing.
# 3. Print the tuple in reverse using a slice.
### Your code starts here.
t = ('a', 'b', 'c', 'd', 'e')
### Your code ends here.
a e
('b', 'c', 'd')
('e', 'd', 'c', 'b', 'a')
6.3.3. Immutability#
Tuples are immutable: once a tuple is created, its items cannot be added, removed, or replaced. Operations such as + build a new tuple instead of changing the old one, which is why tuples have no methods like append or remove.
6.3.3.1. Tuples Cannot Change#
If you try to replace an item in a tuple, Python raises a TypeError.
%%expect TypeError
invoice_key = ("INV-2026-0142", "ACME", "2026-02-28")
print("Invoice key[0]:", invoice_key[0])
invoice_key[0] = "INV-2026-0143" # Tuple does not support item assignment
Invoice key[0]: INV-2026-0142
TypeError: 'tuple' object does not support item assignment
To get a tuple with a different value, build a new one. Slicing and + work on tuples the same way they work on lists:
point = (3, 4, 5)
new_point = point[:2] + (0,) # a new tuple; point is unchanged
print(point, new_point)
(3, 4, 5) (3, 4, 0)
### Exercise: Tuples Cannot Change *(basic)*
# Given date = (2026, 9, 28):
# 1. Build a new tuple next_day with the same year and month and the day plus 1.
# 2. Print both tuples to show that date is unchanged.
date = (2026, 9, 28)
### Your code starts here.
### Your code ends here.
(2026, 9, 28) (2026, 9, 29)
6.3.3.2. Tuples That Contain Lists#
Immutability applies to the tuple itself: which objects it holds, and in what order. If one of those objects is mutable, such as a list, that object can still change. This is the same idea as the shallow copies in Section 6.2: the outer container is fixed, but the list inside it is a shared object that can be modified.
record = ('Ana', [91, 84]) # a name and a list of scores
scores = record[1]
scores.append(77) # changes the list inside the tuple
print(record)
print(scores is record[1]) # True: the same list object
('Ana', [91, 84, 77])
True
The tuple still holds the same two objects, but the list it holds has changed. Replacing the list itself is still an error:
%%expect TypeError
record[1] = [100]
TypeError: 'tuple' object does not support item assignment
If a record must never change, store only immutable values inside it, such as numbers, strings, and other tuples: ('Ana', (91, 84)).
### Exercise: Tuples That Contain Lists *(applied)*
# Given team = ('Tigers', ['Ann', 'Bo']):
# 1. Add 'Cy' to the member list inside team, then print team.
# 2. In a comment, explain why team[1] = ['Di'] would raise a TypeError
# even though step 1 worked.
# 3. Build frozen_team, a version of team whose members are stored in a
# tuple, so nothing in it can change. Print it.
team = ('Tigers', ['Ann', 'Bo'])
### Your code starts here.
### Your code ends here.
('Tigers', ['Ann', 'Bo', 'Cy'])
('Tigers', ('Ann', 'Bo', 'Cy'))
6.3.4. Unpacking and Iteration Helpers#
Tuples make it easy to pull values out of a sequence and to work with several sequences at once. This section covers unpacking a tuple into separate variables, starred unpacking, and two built-in helpers, zip() and enumerate(), that produce tuples as you loop.
6.3.4.1. Basic Unpacking#
You can assign multiple variables from a tuple or any sequence.
a, b = 1, 2
Values are assigned left to right. In this example, a gets 1 and b gets 2.
You can display the results like this:
a, b
(1, 2)
More generally, if the left side is a tuple of variables, the right side can be any sequence - a string, list, or tuple. For example, to split an email address into a user name and domain:
email = 'monty@python.org'
username, domain = email.split('@')
split returns a list with two elements; the first goes to username, the second to domain.
username, domain
('monty', 'python.org')
The number of variables on the left and values on the right must match; otherwise you get a ValueError.
%%expect ValueError
a, b = 1, 2, 3
ValueError: too many values to unpack (expected 2)
Tuple assignment is a clean way to swap two variables. With conventional assignments, you need a temporary variable:
temp = a
a = b
b = temp
That works, but tuple assignment does the same swap without a temporary variable. It works because Python evaluates the whole right side, b, a, before it assigns anything.
a, b = b, a
### Exercise: Tuple Assignment *(basic)*
# 1. Unpack the tuple (10, 20, 30) into three variables x, y, z and print them.
# 2. Split the email address 'ada@lovelace.org' into username and domain using
# split('@') and tuple assignment, then print both.
# 3. Swap the values of two variables a=5, b=99 using tuple assignment.
### Your code starts here.
### Your code ends here.
10 20 30
ada lovelace.org
99 5
6.3.4.2. Starred Unpacking#
Use a starred name to capture “the rest” of a sequence during assignment.
first, *middle, last = range(6)
first, middle, last
(0, [1, 2, 3, 4], 5)
Tuple assignment is also handy in for loops. For example, here is a list of (name, score) records:
records = [('Ana', 91), ('Bob', 84), ('Chen', 77)]
for record in records:
name, score = record
print(name, '->', score)
Ana -> 91
Bob -> 84
Chen -> 77
Each time through the loop, record is a tuple with a name and a score. You can unpack it directly in the for statement:
for name, score in records:
print(name, '->', score)
Ana -> 91
Bob -> 84
Chen -> 77
Now name and score are assigned on each iteration.
### Exercise: Starred Unpacking *(applied)*
# Given data = (1, 2, 3, 4, 5, 6)
# 1. Unpack the first element into `head`, the last into `tail`,
# and everything in between into `body` using starred unpacking.
# 2. Print all three.
# 3. Loop over [('x', 10), ('y', 20), ('z', 30)] and unpack
# each pair directly in the for statement, printing each pair.
### Your code starts here.
data = (1, 2, 3, 4, 5, 6)
### Your code ends here.
1 [2, 3, 4, 5] 6
x -> 10
y -> 20
z -> 30
6.3.4.3. 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 their actual sales and the targets are stored in two separate lists.
actual = [112, 98, 135, 144, 101, 129, 88]
target = [120, 120, 120, 120, 120, 120, 120]
To count how many weeks the team exceeded its target, use zip, which you saw in Section 6.1. It combines sequences and returns a zip object that pairs elements like the teeth of a zipper, and each pair is a tuple.
type(zip(actual, target))
zip
zip stops when the shortest input runs out. To keep going, use itertools.zip_longest; its fillvalue argument sets the value used for the missing items.
from itertools import zip_longest
list(zip('abc', [1, 2]))
list(zip_longest('abc', [1, 2], fillvalue='-'))
[('a', 1), ('b', 2), ('c', '-')]
You can loop over the zip object to get the pairs.
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).
You 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 you can get the last week’s data like this:
weekly_results[-1]
(88, 120)
6.3.4.4. enumerate()#
Section 6.1 showed how enumerate pairs each element with its index in a loop. Each pair it produces is a tuple of (index, element), which is why you can unpack it in the for statement. The index starts at 0 by default; to start at 1 (like line numbers), pass the optional start argument:
list(enumerate('abc', start=1))
[(1, 'a'), (2, 'b'), (3, 'c')]
### Exercise: Zip *(applied)*
# 1. Given names = ['Alice', 'Bob', 'Carol'] and scores = [88, 95, 72],
# use zip to print each name paired with their score.
# 2. Build a list of (name, score) tuples using list(zip(...)),
# then print the last pair.
# 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
('Carol', 72)
1 Alice
2 Bob
3 Carol
6.3.5. 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, range, etc.) into a tuple. |
|
6.3.5.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 *(basic)*
# 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
6.3.5.2. Tuple Methods#
Tuples have only two methods, count and index, and both return information without changing the tuple. Methods that change a list in place, such as append, remove, and sort, do not exist for tuples.
orders = ('PENDING', 'SHIPPED', 'PENDING', 'DELIVERED')
orders.count('PENDING'), orders.index('DELIVERED') # this returns a tuple
(2, 3)
### Exercise: Tuple Methods *(basic)*
# 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
6.3.5.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 new list, not a tuple. The original tuple is unchanged.
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() # tuples have no sort method, because they cannot be changed
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(type(reversed(orders)))
tuple(reversed(orders))
<class 'reversed'>
('DELIVERED', 'SHIPPED', 'PENDING', 'RECEIVED')
# original object is unchanged
orders
('RECEIVED', 'PENDING', 'SHIPPED', 'DELIVERED')
### Exercise: Tuple Functions *(basic)*
# 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)
6.3.5.4. Comparing and Sorting#
Comparison operators such as < and == 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, you 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.
### Exercise: Comparing and Sorting *(basic)*
# 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')]
6.3.5.5. Sorting with a Key Function#
Sorting a list of tuples compares the first elements first. To sort by a different field, pass a function as the optional key argument to sorted. The key function computes a sort key for each item.
records = [('Dee', 95), ('Bob', 84), ('Ana', 91), ('Chen', 77)]
sorted(records) # sorts by name, the first element
[('Ana', 91), ('Bob', 84), ('Chen', 77), ('Dee', 95)]
def second_element(t):
return t[1]
sorted(records, key=second_element) # sorts by score
[('Chen', 77), ('Bob', 84), ('Ana', 91), ('Dee', 95)]
The lowest score comes first and the highest comes last. If you only want the maximum, you don’t need to sort: max also accepts a key function.
max(records, key=second_element)
('Dee', 95)
### Exercise: Sorting with a Key Function *(applied)*
# Given the list of (product, price) tuples below:
# 1. Sort the products by price, lowest first, and print the result.
# 2. Print the most expensive product using max() with a key function.
# 3. Print the cheapest product using min() with a key function.
products = [('pen', 1.5), ('notebook', 3.25), ('stapler', 7.0), ('eraser', 0.75)]
### Your code starts here.
### Your code ends here.
[('eraser', 0.75), ('pen', 1.5), ('notebook', 3.25), ('stapler', 7.0)]
('stapler', 7.0)
('eraser', 0.75)
6.3.6. Tuples in Function Design#
Functions are one of the most common places to meet tuples. A function can return several results packed into one tuple, and it can accept any number of arguments, which Python collects into a tuple.
6.3.6.1. Tuples as Return Values#
A function returns one object, but that object can be a tuple. Returning a tuple is a common way to provide multiple results.
For example, a scheduling system may need to convert a total number of minutes into hours and minutes.
The built-in function divmod combines // and %: it takes two numbers and returns a tuple with the quotient and the remainder.
divmod(135, 60) # 135 minutes → (2 hours, 15 minutes)
(2, 15)
You can use tuple assignment to store the hours and minutes in separate variables.
hours, minutes = divmod(135, 60)
hours
2
minutes
15
Here is a simple function that returns both the minimum and maximum from a list, which is useful for summarizing a range of values such as daily sales figures.
def min_max(t):
return min(t), max(t)
min and max are built-in functions that find the smallest and largest elements of a sequence.
min_max computes both and returns them as a tuple.
min_max([8500, 12400, 7200, 9900]) # daily sales figures
(7200, 12400)
You can unpack the result like this:
low, high = min_max([8500, 12400, 7200, 9900])
low, high
(7200, 12400)
### Exercise: Tuples as Return Values *(applied)*
# Write a function called `stats` that takes a list of numbers and returns
# a tuple containing (min, max, sum, mean) of the list.
# Test it with [10, 20, 30, 40, 50] and unpack the result into four variables.
### Your code starts here.
### Your code ends here.
10 50 150 30.0
6.3.6.2. Argument Packing#
Functions can take a variable number of arguments.
A parameter name that begins with * packs extra arguments into a tuple.
The following function takes any number of arguments and computes their arithmetic mean. (Python’s statistics module already has a mean function; writing one here shows how packing works.)
def mean(*args): # variable-length argument list
return sum(args) / len(args)
The parameter name can be anything, but args is conventional.
Here is a call with four daily sales figures:
mean(8500, 12400, 7200, 9900) # average of four daily sales figures
9500.0
If you have a sequence of values and want to pass them as separate arguments, use * to unpack the sequence.
For example, divmod takes exactly two arguments - if you pass a tuple, it counts as a single argument and raises an error.
%%expect TypeError
t = (135, 60)
divmod(t)
TypeError: divmod expected 2 arguments, got 1
Even though the tuple contains two elements, it is still one argument. If you unpack it, the two elements are passed separately.
divmod(*t)
(2, 15)
Packing and unpacking are handy when you want to adapt an existing function. For example, this function takes any number of arguments, removes the lowest and highest, and computes the mean of the rest.
def trimmed_mean(*args):
low, high = min_max(args)
trimmed = list(args) # convert to a list for mutability
trimmed.remove(low)
trimmed.remove(high)
return mean(*trimmed)
First it uses min_max to find the lowest and highest values.
Then it converts args to a list so it can use remove.
Finally it unpacks the trimmed list so the elements are passed to mean as separate arguments.
Here is an example using judge scores, where one score is unusually low:
mean(8.5, 9.2, 8.8, 4.5) # raw mean including the outlier
7.75
trimmed_mean(8.5, 9.2, 8.8, 4.5) # mean after dropping lowest (4.5) and highest (9.2)
8.65
This kind of trimmed mean is used in sports with subjective judging (like diving and gymnastics) to reduce the influence of outlier scores.
You can also unpack sequences to build new tuples (or lists) without using +.
For example, merging two quarterly sales tuples into a single half-year tuple:
q1 = (112, 98, 135) # monthly sales for Q1
q2 = (144, 101, 129) # monthly sales for Q2
h1 = (*q1, *q2) # combined first-half data
h1
(112, 98, 135, 144, 101, 129)
### Exercise: Argument Packing *(applied)*
# 1. Write a function total_sales(*amounts) that returns the sum of any
# number of sales amounts.
# 2. Call it with 112, 98, and 135 and print the result.
# 3. Call it again by unpacking the tuple q2 = (144, 101, 129) with *.
q2 = (144, 101, 129)
### Your code starts here.
### Your code ends here.
345
374
6.3.7. Tuples as Records#
A record is a group of related values that describe one thing, such as one sale or one student. Tuples fit records well: the fields stay together, their order is fixed, and the record cannot be changed by accident. A list of tuples works like a small table, with one tuple per row.
6.3.7.1. Lists of Tuples#
Data often arrives as lines of text. Each line below describes one sale: the region, the month, and the amount.
lines = [
'North,Jan,112',
'South,Jan,98',
'North,Feb,135',
'West,Jan,144',
'South,Feb,101',
]
sales = []
for line in lines:
region, month, amount = line.split(',')
sales.append((region, month, int(amount)))
sales
[('North', 'Jan', 112),
('South', 'Jan', 98),
('North', 'Feb', 135),
('West', 'Jan', 144),
('South', 'Feb', 101)]
Each row is now a (region, month, amount) tuple. You can loop over the rows with unpacking:
for region, month, amount in sales:
print(region, month, amount)
North Jan 112
South Jan 98
North Feb 135
West Jan 144
South Feb 101
You can also filter the rows with a list comprehension and sort them with a key function:
north = [row for row in sales if row[0] == 'North']
print(north)
def amount_of(row):
return row[2]
print(sorted(sales, key=amount_of)[-2:]) # the two largest sales, smallest first
[('North', 'Jan', 112), ('North', 'Feb', 135)]
[('North', 'Feb', 135), ('West', 'Jan', 144)]
### Exercise: Lists of Tuples *(basic)*
# Given lines = ['Ana,91', 'Bob,84', 'Chen,77', 'Dee,95']:
# 1. Build a list of (name, score) tuples called students,
# converting each score to an int.
# 2. Use a list comprehension to print the names of students
# who scored 85 or above.
# 3. Print the student with the lowest score using min() with a key function.
lines = ['Ana,91', 'Bob,84', 'Chen,77', 'Dee,95']
### Your code starts here.
### Your code ends here.
['Ana', 'Dee']
('Chen', 77)
6.3.7.2. Named Tuples#
Positions such as row[2] are easy to mix up: nothing in the code says that index 2 is the amount. A named tuple is a tuple whose fields also have names. The namedtuple function from the collections module creates a new tuple type with the field names you choose.
from collections import namedtuple
Sale = namedtuple('Sale', ['region', 'month', 'amount'])
s = Sale('North', 'Jan', 112)
print(s)
print(s.amount) # access by name
print(s[2]) # access by position still works
Sale(region='North', month='Jan', amount=112)
112
112
A named tuple is still a tuple: it supports indexing, unpacking, and comparison. Converting the rows makes the code easier to read:
sale_rows = [Sale(region, month, amount) for region, month, amount in sales]
for s in sale_rows:
if s.amount > 100:
print(s.region, s.month, s.amount)
North Jan 112
North Feb 135
West Jan 144
South Feb 101
Like any tuple, a named tuple cannot be changed:
%%expect AttributeError
s.amount = 200
AttributeError: can't set attribute
You will meet named tuples again in data work: the pandas method DataFrame.itertuples() returns each row of a table as a named tuple.
### Exercise: Named Tuples *(applied)*
# 1. Create a named tuple type Student with the fields name and score.
# 2. Convert students = [('Ana', 91), ('Bob', 84), ('Chen', 77), ('Dee', 95)]
# into a list of Student named tuples.
# 3. Print the name of the top student using max() with a key function
# that returns the score field.
students = [('Ana', 91), ('Bob', 84), ('Chen', 77), ('Dee', 95)]
### Your code starts here.
### Your code ends here.
Dee
6.3.7.3. Worked Example: Summarizing Sales Records#
The task is to report the total and average sale, the smallest and largest sale, and the two largest sales from sale_rows, using a function that returns several results as a tuple.
def amount_key(sale):
return sale.amount
def summarize(rows):
amounts = [sale.amount for sale in rows]
total = sum(amounts)
average = total / len(amounts)
smallest = min(rows, key=amount_key)
largest = max(rows, key=amount_key)
return total, average, smallest, largest
total, average, smallest, largest = summarize(sale_rows)
top_two = sorted(sale_rows, key=amount_key)[::-1][:2]
print(f'Total: {total}')
print(f'Average: {round(average, 1)}')
print(f'Smallest: {smallest.region} {smallest.month} {smallest.amount}')
print(f'Largest: {largest.region} {largest.month} {largest.amount}')
print('Top two:', [(s.region, s.month) for s in top_two])
Total: 590
Average: 118.0
Smallest: South Jan 98
Largest: West Jan 144
Top two: [('West', 'Jan'), ('North', 'Feb')]
Each step uses a tool from this section:
Named tuples make fields such as
sale.amountreadable.A list comprehension collects the amounts, and
sum()andlen()compute the total and average.min()andmax()with akeyfunction find whole records, not just amounts.summarizereturns four results as one tuple, and tuple assignment unpacks them.sorted()with akey, followed by slicing, ranks the records.
### Exercise: When to Use Tuples *(applied)*
# For each scenario below, decide whether a tuple or a list is more appropriate
# and implement it:
# 1. Store the (latitude, longitude) coordinates of a fixed location: 38.9, -77.0
# 2. Build a collection of city names that will have cities added to it over time.
# Start with ['Paris', 'Tokyo'] and append 'Nairobi'.
# 3. Store holidays as a list of (month, day, name) tuples:
# (1, 1, 'New Year') and (7, 4, 'Independence Day').
# Loop over the list with unpacking and print the name of the
# holiday on July 4.
### Your code starts here.
### Your code ends here.
(38.9, -77.0)
['Paris', 'Tokyo', 'Nairobi']
Independence Day
6.3.8. Summary#
A tuple is an ordered, immutable sequence. Create one with commas (a trailing comma for a single item) or with
tuple().Indexing, slicing,
+,*,in,count(),index(), and built-in functions such aslen()andsorted()work with tuples.A tuple’s items cannot be replaced, but a mutable item inside it, such as a list, can still change.
Tuples are safe to pass to functions; to produce new values, return a new tuple.
Tuple assignment and starred unpacking assign several names at once, and
zip()andenumerate()produce tuples.Tuples compare element by element;
sorted(),min(), andmax()accept akeyfunction to compare by a chosen field.A function can return several results as a tuple, and
*argspacks any number of arguments into a tuple.A list of tuples works like a table of records, and named tuples give each field a name.
6.3.9. Further Reading#
Tuples and Sequences, Python Tutorial
Sequence Types: list, tuple, range, Python Standard Library documentation
collections.namedtuple, Python Standard Library documentationSorting Techniques: Key Functions, Python HOWTOs