15. Tuple type#

Author: Tue Nguyen

15.1. Outline#

  • What is a tuple?

  • Create a tuple

  • Index a tuple

  • Slice a tuple

  • Nested tuples

  • Iterate through a tuple

  • Copy a tuple

  • Operations on a tuple

  • Unpack a tuple

  • Tuple comprehensions

15.2. What is a tuple?#

  • The tuple type is identical to the list type, except that it is immutable

15.3. Create a tuple#

  • To create a list we use ()

  • Elements are separated by commas ,

# Init a tuple
x = (1, 2, 3, 4)

# Print value and type
print(x)
print(type(x))
(1, 2, 3, 4)
<class 'tuple'>
# If a tuple has only one element
# we need to provide an extra comma 
x = (1,)
print(x)
print(type(x))
(1,)
<class 'tuple'>
# without this extra comma, 
# Python will interpret the RHS as number 1
x = (1)
print(x)
print(type(x))
1
<class 'int'>
# We can omit the parentheses
x = 1, 2, 3
print(x)
print(type(x))
(1, 2, 3)
<class 'tuple'>
# A  can hold elements of different types
# including lists and tuple
x = (None, True, 10, 20.5, "ABC", [1, 2], (3, 4))
print(x)
print(type(x))
(None, True, 10, 20.5, 'ABC', [1, 2], (3, 4))
<class 'tuple'>
# If there are too much elements
# You can arrange the elements in multiple lines
x = ("Apple", "Banana", "Orange", "Peach",
     "Guava", "Watermelon", "Pineapple")

print(x)
print(type(x))
('Apple', 'Banana', 'Orange', 'Peach', 'Guava', 'Watermelon', 'Pineapple')
<class 'tuple'>

15.4. Index a tuple#

  • Same as indexing a list

  • However, since a tuple is immutable, we don’t have indexing (write)

# Init a tuple
x = (1, 2, 3, 4, 5)
x
(1, 2, 3, 4, 5)
# Get the first element
x[0]
1
# Get the third element
x[2]
3
# Get the last element
x[-1]
5

15.5. Slice a tuple#

  • Same as slicing a list (but return a tuple)

  • We also don’t have slicing (write)

# Init a tuple
x = (1, 2, 3, 4, 5)
x
(1, 2, 3, 4, 5)
# First 3 elements
x[:3]
(1, 2, 3)
# Last 3 elements
x[-3:]
(3, 4, 5)
# From beginning to end 
# but skip one between each jump
x[::2]
(1, 3, 5)
# No out-of-range error for slicing
x[1:1000]
(2, 3, 4, 5)

15.6. Nested tuples#

  • Same as lists, a tuple can contains other iterables including lists and tuples

# Init
x = (1, 2, (3, 4), [5, 6])
print(x)
(1, 2, (3, 4), [5, 6])
# Access the inner tuple
x[2]
(3, 4)
# Access the first element of inner tuple
x[2][0]
3
# Access the inner list
x[-1]
[5, 6]
# Access the last element of inner list
x[-1][-1]
6

15.7. Iterate through a tuple#

  • Same as lists, we can use a for loop

# Init a tuple
x = (1, 2, 3, 4, 5)
# Print out squares of elements
for e in x:
    print(e**2)
1
4
9
16
25

15.8. Mutablity#

  • A tuple is immutable, meaning that we cannot change it

  • Each element hold a reference to an object, and that reference stays the same forever

  • However, if one of the element is of mutable type, then we can still modify that mutable object

  • Let’s consider some examples

# Init
x = (1, 2, [3, 4])
print(x)
print(type(x))
(1, 2, [3, 4])
<class 'tuple'>
# This tuple holds three elements pointing to 3 objects
# with the following IDs
[id(e) for e in x]
[139636787859744, 139636787859776, 139636382749184]
# If you try to modify an element through an assignment 
# you will get an error
# Try with x[0] = "A" or x[-1] = "A"
# Notice that the last element of x is a list
x[-1]
[3, 4]
# A list is mutable, thus we can modify this list
# Ex: replace the first element of this list with "A"
x[-1][0] = "A"
# We see the change has been saved
x
(1, 2, ['A', 4])
# But it doesn't mean the tuple changes
# The tuple still holding the same references as before
[id(e) for e in x]
[139636787859744, 139636787859776, 139636382749184]

15.9. Copy a tuple#

15.9.1. Shallow copying#

  • There’s no way to make a shallow copy of a tuple

  • A tuple doesn’t have method .copy()

  • If you use y = x[:] or y = tuple(x), it’s aliasing, NOT copying

# Init a tuple
x = (1, 2, [3, 4])
print(x)
print(id(x))
(1, 2, [3, 4])
139636702385856
# Try with y = x[:]
y = x[:]
print(id(x))
print(id(y))
print(x is y)
139636702385856
139636702385856
True
# Try with y = tuple(x)
y = x[:]
print(id(x))
print(id(y))
print(x is y)
139636702385856
139636702385856
True

15.9.2. Deep copying#

Same as lists, we use copy.deepcopy()

# Init 
x = (1, 2, [3, 4])
print(x)
print(type(x))
print(id(x))
(1, 2, [3, 4])
<class 'tuple'>
139636382833792
# Import module copy
import copy
# Make a deep copy
y = copy.deepcopy(x)
# Verify that x and y have the same values
print(x)
print(y)
(1, 2, [3, 4])
(1, 2, [3, 4])
# But they are different object
print(id(x))
print(id(y))
print(x is y)
139636382833792
139636382406144
False
# Now change 1st element of last element of x to False
x[-1][0] = False
# We see that only x changes, y stays the same
print(x)
print(y)
(1, 2, [False, 4])
(1, 2, [3, 4])

15.10. Operations on a tuple#

  • Since tuples are immutale, we don’t have inplace operations

  • Other regular operations are the same as lists

15.11. Unpack a tuple#

Same as lists

# Init a tuple
my_date = (2022, 4, 1)
my_date
(2022, 4, 1)
# Unpack all
y, m, d = my_date
print(y, m, d)
2022 4 1
# Get date, month only
_, m, d = my_date
print(m, d)
4 1
# Get year only
y, *_ = my_date
print(y)
2022
# Get date only
*_, d = my_date
print(d)
1

15.12. Enumerate#

  • When iterating through a sequence, we might want to access not only the elements but also their indexes

  • If so, we can use enumerate()

x = (3, 0, -4, 5, -2)
for i, v in enumerate(x):
    print(f"Element {i + 1} has value {v}")
Element 1 has value 3
Element 2 has value 0
Element 3 has value -4
Element 4 has value 5
Element 5 has value -2

What happened?

  • At each iteration enumerate() returns a 2-element tuple

    • The first element is the index

    • The second element is the value

  • When we write i, v as in the for statement above, we are unpacking the tuple into i and v

15.13. Tuple comprehensions#

  • Same as list comprehensions

  • But the result will be a generator (for better performance)

  • We will learn about generators later

# Int
x = (1, 2, 5, -1, 4, 0, 7, -3, -2)
x
(1, 2, 5, -1, 4, 0, 7, -3, -2)
# Get squares of elements of x
(e**2 for e in x)
<generator object <genexpr> at 0x7effa0fa9430>
# We can use typecasting to convert it into a tuple
tuple(e**2 for e in x)
(1, 4, 25, 1, 16, 0, 49, 9, 4)
# Get odd numbers only
tuple(e for e in x if e % 2 != 0)
(1, 5, -1, 7, -3)
# Get even and non-negative numbers only
tuple(e for e in x if (e % 2 == 0) and (e >= 0))
(2, 4, 0)

15.14. Summary#

What is a list?

  • An immutable sequence

  • Same as a list, except immutable

Create a tuple

  • Method 1: x = (1, 2, 3)

  • Method 2: x = 1, 2, 3

  • For tuple of one element: x = 1, or x = (1,)

Index a tuple

  • Same as lists

  • No indexing (write) operations

Slice a tuple

  • Same as lists

  • No slicing (write) operations

Nested tuple

  • Same as lists, a tuple can contains other iterables including lists and tuples

Iterate through a tuple

  • Same as lists

Mutability

  • A tuple is immutable, meaning that we cannot change it

  • Each element hold a reference to an object, and that reference stays the same forever

  • However, if one of the element is of mutable type, then we can still modify that mutable object

Copy a tuple

  • No way to perform shallow copying on a tuple

    • Tuples don’t have .copy() method

    • y = x[:] and y = tuple(x) is aliasing, not copying

  • Deep copying is the same as with lists

Operations on a tuple

  • No inplace operations

  • Regular operations are the same as lists

Unpack a tuple

  • Same as lists

Tuple comprehensions

  • Same as lists, except that a generator will be returned

15.15. Practice#

To be updated