Python Tuples

by Pyrastra Team
Python Tuples

In the previous two lessons, we introduced Python lists, which are container-type data types. Through list-type variables, we can store multiple data items and implement batch operations on data through loops. Of course, Python has other container-type data types. Next, we’ll introduce another container-type data type called tuple.

Tuple Definition and Operations

In Python, a tuple is also a sequence composed of multiple elements arranged in a certain order. The difference between tuples and lists is that tuples are immutable types, which means that once a tuple-type variable is defined, the elements cannot be added or deleted, and the values of elements cannot be modified. If you try to modify elements in a tuple, it will raise a TypeError error, causing the program to crash. Tuples are typically defined using literal syntax like (x, y, z). Tuples support the same operators as lists. Let’s look at the code below.

# Define a three-element tuple
t1 = (35, 12, 98)
# Define a four-element tuple
t2 = ('Luo Hao', 45, True, 'Chengdu, Sichuan')

# Check the type of variables
print(type(t1))  # <class 'tuple'>
print(type(t2))  # <class 'tuple'>

# Check the number of elements in tuples
print(len(t1))  # 3
print(len(t2))  # 4

# Indexing operations
print(t1[0])    # 35
print(t1[2])    # 98
print(t2[-1])   # Chengdu, Sichuan

# Slicing operations
print(t2[:2])   # ('Luo Hao', 43)
print(t2[::3])  # ('Luo Hao', 'Chengdu, Sichuan')

# Loop through elements in tuple
for elem in t1:
    print(elem)

# Membership operations
print(12 in t1)         # True
print(99 in t1)         # False
print('Hao' not in t2)  # True

# Concatenation operations
t3 = t1 + t2
print(t3)  # (35, 12, 98, 'Luo Hao', 43, True, 'Chengdu, Sichuan')

# Comparison operations
print(t1 == t3)            # False
print(t1 >= t3)            # False
print(t1 <= (35, 11, 99))  # False

If a tuple has two elements, we call it a two-tuple; if a tuple has five elements, we call it a five-tuple. It’s important to note that () represents an empty tuple, but if a tuple has only one element, you need to add a comma. Otherwise, () doesn’t represent tuple literal syntax, but rather parentheses that change operation precedence. So ('hello', ) and (100, ) are one-tuples, while ('hello') and (100) are just a string and an integer. We can verify this with the code below.

a = ()
print(type(a))  # <class 'tuple'>
b = ('hello')
print(type(b))  # <class 'str'>
c = (100)
print(type(c))  # <class 'int'>
d = ('hello', )
print(type(d))  # <class 'tuple'>
e = (100, )
print(type(e))  # <class 'tuple'>

Packing and Unpacking Operations

When we assign multiple comma-separated values to a variable, the multiple values are packed into a tuple type; when we assign a tuple to multiple variables, the tuple is unpacked into multiple values and then assigned to the corresponding variables, as shown in the code below.

# Packing operation
a = 1, 10, 100
print(type(a))  # <class 'tuple'>
print(a)        # (1, 10, 100)
# Unpacking operation
i, j, k = a
print(i, j, k)  # 1 10 100

When unpacking, if the number of unpacked elements doesn’t match the number of variables, it will raise a ValueError exception with error messages: too many values to unpack (too many values to unpack) or not enough values to unpack (not enough values to unpack).

a = 1, 10, 100, 1000
# i, j, k = a             # ValueError: too many values to unpack (expected 3)
# i, j, k, l, m, n = a    # ValueError: not enough values to unpack (expected 6, got 4)

There’s a solution when the number of variables is less than the number of elements: use a starred expression. Through starred expressions, we can let one variable receive multiple values, as shown in the code below. Two things to note: first, a variable modified with a starred expression becomes a list with 0 or more elements; second, in unpacking syntax, a starred expression can only appear once.

a = 1, 10, 100, 1000
i, j, *k = a
print(i, j, k)        # 1 10 [100, 1000]
i, *j, k = a
print(i, j, k)        # 1 [10, 100] 1000
*i, j, k = a
print(i, j, k)        # [1, 10] 100 1000
*i, j = a
print(i, j)           # [1, 10, 100] 1000
i, *j = a
print(i, j)           # 1 [10, 100, 1000]
i, j, k, *l = a
print(i, j, k, l)     # 1 10 100 [1000]
i, j, k, l, *m = a
print(i, j, k, l, m)  # 1 10 100 1000 []

It should be noted that unpacking syntax works for all sequences, which means that lists, range sequences constructed by the range function, and even strings can all use unpacking syntax. You can try running the code below to see what results appear.

a, b, *c = range(1, 10)
print(a, b, c)
a, b, c = [1, 10, 100]
print(a, b, c)
a, *b, c = 'hello'
print(a, b, c)

Swapping Variable Values

Swapping variable values is a frequently used operation when writing code. In many programming languages, swapping the values of two variables requires an intermediate variable. If you don’t use an intermediate variable, you need to use rather obscure bitwise operations. In Python, swapping the values of two variables a and b only requires the code shown below.

a, b = b, a

Similarly, if you want to swap the values of three variables a, b, c, i.e., assign the value of b to a, the value of c to b, and the value of a to c, you can do the same.

a, b, c = b, c, a

It should be noted that the operations above don’t use packing and unpacking syntax. Python’s bytecode instructions have instructions like ROT_TWO and ROT_THREE that can directly implement this operation with very high efficiency. However, if there are more than three variables whose values need to be swapped in sequence, there are no directly available bytecode instructions, and you need to complete the value swapping between variables through packing and unpacking.

Comparing Tuples and Lists

There’s a very worthwhile question to discuss here: Python already has the list type, so why do we need a type like tuples? This question seems a bit difficult for beginners, but it doesn’t matter. Let’s present the viewpoints first, and you can gradually understand them as you learn.

  1. Tuples are immutable types, and immutable types are more suitable for multi-threaded environments because they reduce the synchronization overhead of concurrent access to variables. We’ll discuss this point together when we cover concurrent programming later.

  2. Tuples are immutable types, and typically immutable types are superior to corresponding mutable types in creation time. We can use the timeit function from the timeit module to see how much time it takes to create tuples and lists that store the same elements. The number parameter of the timeit function indicates the number of times the code is executed. In the code below, we created lists and tuples storing integers from 1 to 9, executing each operation 10000000 times and recording the execution time.

    import timeit
    
    print('%.3f seconds' % timeit.timeit('[1, 2, 3, 4, 5, 6, 7, 8, 9]', number=10000000))
    print('%.3f seconds' % timeit.timeit('(1, 2, 3, 4, 5, 6, 7, 8, 9)', number=10000000))

    Output:

    0.635 seconds
    0.078 seconds

    Note: The execution results of the code above vary depending on hardware and software systems. On the computer I’m currently using, executing the list creation operation 10000000 times takes 0.635 seconds, while executing the tuple creation operation 10000000 times takes 0.078 seconds. Creating tuples is clearly faster, and there’s an order of magnitude difference in time. You can execute this code on your own computer and post your execution results in the comments to see whose computer is more powerful.

Of course, Python’s tuple and list types can be converted to each other. We can complete this operation through the code below.

infos = ('Luo Hao', 43, True, 'Chengdu, Sichuan')
# Convert tuple to list
print(list(infos))  # ['Luo Hao', 43, True, 'Chengdu, Sichuan']

frts = ['apple', 'banana', 'orange']
# Convert list to tuple
print(tuple(frts))  # ('apple', 'banana', 'orange')

Summary

Both lists and tuples are container-type data types, meaning one variable can store multiple data items, and they are both ordered containers that organize elements in a certain order. Lists are mutable data types, while tuples are immutable data types, so lists can add elements, delete elements, clear elements, sort and reverse, but these operations don’t apply to tuples. Both lists and tuples support concatenation operations, membership operations, indexing operations, slicing operations, and other operations. The string type we’ll cover later also supports these operations because strings are sequences of characters arranged in a certain order, and in this respect, the three are no different. We recommend using list comprehension syntax to create lists - it’s not only easy to use but also very efficient, and it’s a very distinctive syntax feature of the Python language.