6. Tuples#

Hide code cell source

# Course setup header — run this cell before the others

import sys
from pathlib import Path

current = Path.cwd()
for parent in [current, *current.parents]:
    if (parent / '_config.yml').exists():
        project_root = parent  # ← Add project root, not chapters
        break
else:
    project_root = Path.cwd().parent.parent

sys.path.insert(0, str(project_root))


from shared import thinkpython, diagram, jupyturtle, download, structshape

# Register as top-level modules so direct imports work in subsequent cells
sys.modules['thinkpython'] = thinkpython
sys.modules['diagram'] = diagram
sys.modules['jupyturtle'] = jupyturtle
sys.modules['download'] = download
sys.modules['structshape'] = structshape

This chapter introduces tuples, a built-in sequence type, and shows how tuples work alongside lists and dictionaries. You will practice tuple assignment and the packing and unpacking operators used with variable-length argument lists.

By the end of this chapter, you will be able to:

  1. Create tuples using literals, packing, and the tuple() constructor

  2. Unpack tuples (including starred unpacking) in assignments and loops

  3. Use zip(), enumerate(), divmod(), and other tuple-aware built-ins

  4. Choose between tuples and lists based on mutability needs

  5. Return multiple values from a function using tuples

One note: There are two common pronunciations of “tuple”. Some people say “tuh-ple” (rhymes with “supple”). In programming, many people say “too-ple” (rhymes with “quadruple”).

6.1. What is a Tuple?#

  • 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.

  • Tuples are hashable when all of their elements are hashable.

Tuples are commonly used to group related values together. For example, a latitude and longitude pair naturally belongs together:

location = (42.3601, -71.0589)

A tuple is a sequence, just like list. When the data belongs together and shouldn’t change - like a coordinate, a date, or a name/age pair - use a tuple. If you need to add, remove, or modify items, use a list.

Feature

Tuple

List

Syntax

(1, 2, 3)

[1, 2, 3]

Mutable

No

Yes

Hashable

Yes (conditional)

No

Methods

2 (.count, .index)

Many

Use case

Fixed data, records

Collections that change

Performance

Faster

Slower

6.1.1. Immutability#

Tuples are immutable, which is the key characteristic of tuples. Once created, items cannot be added, removed, or changed within a tuple, but new tuples can be created as a result of operations. That’s why tuples lack list methods that modify in place, like append and remove.

If you try to modify a tuple with indexing, 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

6.2. Creating 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

(1, 2, 3)

(1, 2, 3)

Write comma-separated values without parentheses

Tuple packing

1, 2, 3

(1, 2, 3)

Use empty parentheses

Empty tuple

()

()

Add a trailing comma for one value

Single-item tuple

(1,) or 1,

(1,)

Use the tuple constructor on an iterable

Constructor

tuple([1, 2, 3])

(1, 2, 3)

6.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.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.2.3. tuple() Constructor#

Another way to create a tuple is with the built-in function/type constructor tuple. With no argument, it creates an empty tuple.

If the argument is a sequence (list, tuple, or string), the tuple constructor returns a tuple containing the elements of that sequence — and list works the same way in reverse.

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'>

6.2.4. Empty Tuple#

t = tuple()

print(t)
print(type(t))
()
<class 'tuple'>

6.2.5. 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
#   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.

Hide code cell source

### solution

colors = ("red", "green", "blue")
one = (42,)
letters = tuple("hello")

print(colors, type(colors))
print(one, type(one))
print(letters, type(letters))
('red', 'green', 'blue') <class 'tuple'>
(42,) <class 'tuple'>
('h', 'e', 'l', 'l', 'o') <class 'tuple'>

6.3. Accessing Elements#

6.3.1. 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
#   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.

Hide code cell source

### solution

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

print(t[0], t[-1])    # first and last
print(t[1:4])          # middle three
print(t[::-1])         # reversed
a e
('b', 'c', 'd')
('e', 'd', 'c', 'b', 'a')

6.4. Tuple Unpacking#

6.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. We 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.

a, b = b, a
### Exercise: Tuple Assignment
#   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.

Hide code cell source

### solution

x, y, z = (10, 20, 30)
print(x, y, z)                                  # 10 20 30

username, domain = 'ada@lovelace.org'.split('@')
print(username, domain)                          # ada lovelace.org

a, b = 5, 99
a, b = b, a
print(a, b)                                      # 99 5
10 20 30
ada lovelace.org
99 5

6.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)

This works because all expressions on the right side are evaluated before any assignments.

Tuple assignment is also handy in for loops. For example, to loop through the items in a dictionary, use the items method.

d = {'one': 1, 'two': 2}
print(type(d.items()))
for item in d.items():          # items() returns a dict_items view of key-value pairs
    key, value = item
    print(key, '->', value)
<class 'dict_items'>
one -> 1
two -> 2

Each time through the loop, item is a tuple with a key and its value. We can unpack it directly:

for key, value in d.items():
    print(key, '->', value)
one -> 1
two -> 2
### Exercise: Starred Unpacking
#   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}.items() and unpack
#      each key-value pair directly in the for statement, printing each pair.
### Your code starts here.

data = (1, 2, 3, 4, 5, 6)



### Your code ends here.

Hide code cell source

### solution

data = (1, 2, 3, 4, 5, 6)

head, *body, tail = data
print(head, body, tail)      # 1 [2, 3, 4, 5] 6

for key, value in {'x': 10, 'y': 20, 'z': 30}.items():
    print(key, '->', value)
1 [2, 3, 4, 5] 6
x -> 10
y -> 20
z -> 30

Now key and value are assigned on each iteration.

6.5. Tuple Operations#

Common tuple operations include:

Python Expression

Description

Example

len(tuple)

Returns the number of items in the tuple.

len((1, 2, 3)) returns 3.

tuple1 + tuple2

Concatenation: combines two tuples into a new tuple.

(1, 2) + (3, 4) returns (1, 2, 3, 4).

tuple * n

Repetition: repeats tuple elements n times.

('Hi!',) * 3 returns ('Hi!', 'Hi!', 'Hi!').

item in tuple

Membership: checks whether a value exists in the tuple (True/False).

3 in (1, 2, 3) returns True.

tuple[index]

Indexing: accesses an element using positive or negative index.

('a', 'b', 'c')[0] returns 'a'.

tuple[start:stop]

Slicing: extracts a range of elements as a new tuple.

(1, 2, 3, 4)[1:3] returns (2, 3).

tuple(iterable)

Constructor: converts an iterable (list, string, set, etc.) into a tuple.

tuple([1, 2]) returns (1, 2).

6.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
#   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.

Hide code cell source

### solution

print((1, 2, 3) + (4, 5, 6))        # concatenation
print(('ha',) * 3)                   # repetition
print(7 in (1, 3, 5, 7, 9))         # membership
(1, 2, 3, 4, 5, 6)
('ha', 'ha', 'ha')
True

Based on these examples, tuples can look a lot like lists.

6.5.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.

Hide code cell source

### solution

t = (3, 1, 4, 1, 5, 9, 2, 6, 5, 3)

print(t.count(1))     # 2
print(t.index(9))     # 5
print(t.count(5))     # 2
2
5
2

6.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

len(tuple)

Returns the total number of items in the tuple.

Works for any tuple.

max(tuple)

Returns the largest item in the tuple.

Elements must be comparable.

min(tuple)

Returns the smallest item in the tuple.

Elements must be comparable.

sum(tuple)

Returns the sum of all numeric elements in the tuple.

Elements should be numbers.

sorted(tuple)

Returns a new list with tuple elements in sorted order.

Output type is list, not tuple.

reversed(tuple)

Returns an iterator over tuple elements in reverse order.

Convert with tuple(...) or list(...) if needed.

tuple(iterable)

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 0x113171810>
('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.

Hide code cell source

### solution

t = (4, 7, 2, 9, 1, 5)

print(len(t), max(t), min(t), sum(t))  # 6 9 1 28
print(sorted(t))                         # [1, 2, 4, 5, 7, 9]
print(tuple(reversed(t)))                # (5, 1, 9, 2, 7, 4)
6 9 1 28
[1, 2, 4, 5, 7, 9]
(5, 1, 9, 2, 7, 4)

6.5.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 0x11318de80>

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)

6.5.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.

Hide code cell source

### solution

names = ['Alice', 'Bob', 'Carol']
scores = [88, 95, 72]

for name, score in zip(names, scores):
    print(name, score)

scoreboard = dict(zip(names, scores))
print(scoreboard)

for rank, name in enumerate(names, start=1):
    print(rank, name)
Alice 88
Bob 95
Carol 72
{'Alice': 88, 'Bob': 95, 'Carol': 72}
1 Alice
2 Bob
3 Carol

6.5.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.

Hide code cell source

### solution

people = [('Smith', 'John'), ('Adams', 'Zara'), ('Smith', 'Alice'), ('Adams', 'Alan')]
print(sorted(people))
# Python compares the first element (last name) first;
# if equal, it compares the second element (first name).
# Result: [('Adams', 'Alan'), ('Adams', 'Zara'), ('Smith', 'Alice'), ('Smith', 'John')]
[('Adams', 'Alan'), ('Adams', 'Zara'), ('Smith', 'Alice'), ('Smith', 'John')]

6.5.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.

Hide code cell source

### solution

counter = value_counts('mississippi')
items = counter.items()

def second_element(t):
    return t[1]

print(sorted(items, key=second_element))   # sorted by frequency ascending
print(max(items, key=second_element))      # ('i', 4) — most frequent letter
[('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.

6.6. Tuples and Functions#

6.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 (// and %) takes two arguments and returns a tuple with the quotient and remainder.

divmod(135, 60)    # 135 minutes → (2 hours, 15 minutes)
(2, 15)

We 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 — 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)

We can unpack the result like this:

low, high = min_max([8500, 12400, 7200, 9900])
low, high
(7200, 12400)
### Exercise: Tuples as Return Values
#   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.

Hide code cell source

def stats(numbers):
    return min(numbers), max(numbers), sum(numbers), sum(numbers) / len(numbers)

lo, hi, total, avg = stats([10, 20, 30, 40, 50])
print(lo, hi, total, avg)    # 10 50 150 30.0
10 50 150 30.0

6.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 (pretending we don’t have a built-in for it) takes any number of arguments and computes their arithmetic mean.

def mean(*args):                # variable-length argument list
    return sum(args) / len(args)

The parameter name can be anything, but args is conventional. Here we call it with three employee performance scores:

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

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)

This kind of trimmed mean is used in sports with subjective judging (like diving and gymnastics) to reduce the influence of outlier scores.

### Exercise: When to Use Tuples
#   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. Use a (month, day) tuple as a key in a dictionary to store holiday names.
#      Add entries for (1,1)->'New Year' and (7,4)->'Independence Day'.
#      Print the holiday for July 4th.
### Your code starts here.




### Your code ends here.

Hide code cell source

# 1. Tuple — fixed record, won't change
location = (38.9, -77.0)
print(location)

# 2. List — will grow over time
cities = ['Paris', 'Tokyo']
cities.append('Nairobi')
print(cities)

# 3. Tuple as dict key — tuples are hashable
holidays = {(1, 1): 'New Year', (7, 4): 'Independence Day'}
print(holidays[(7, 4)])    # Independence Day
(38.9, -77.0)
['Paris', 'Tokyo', 'Nairobi']
Independence Day