Skip to content

Latest commit

 

History

History
173 lines (130 loc) · 8.66 KB

File metadata and controls

173 lines (130 loc) · 8.66 KB

Common Data Structures: Tuples

In the previous two lessons, we talked about lists in Python. A list is a container-type data type. Through variables of list type, we can store multiple pieces of data and use loops to process data in batches. Of course, Python also has other container-type data types. Next, we will talk about another one, and its name is the tuple.

Defining Tuples and Basic Operations

In Python, a tuple is also a sequence made up of multiple elements in a certain order. The difference between a tuple and a list is that a tuple is an immutable type. This means that once a variable of tuple type has been defined, elements cannot be added or deleted, and the values of the elements cannot be changed either. If you try to modify an element in a tuple, it raises TypeError and causes the program to crash. Tuples are usually defined with literal syntax like (x, y, z), and the operators supported by tuples are the same as those supported by lists. Look at the code below.

# A tuple with three elements.
t1 = (35, 12, 98)
# A tuple with four elements.
t2 = ('Luo Hao', 45, True, 'Chengdu, Sichuan')

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

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

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

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

# Loop through the elements in the 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
t3 = t1 + t2
print(t3)

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

If a tuple has two elements, we call it a 2-tuple. If a tuple has five elements, we call it a 5-tuple. One thing that needs special attention is this: () means the empty tuple, but if a tuple has only one element, then we need to add a comma. Otherwise, () does not mean tuple literal syntax. It means parentheses used to change operator precedence. So ('hello', ) and (100, ) are 1-tuples, while ('hello') and (100) are only 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

When we assign multiple values separated by commas to one variable, the multiple values are packed into a tuple. When we assign one tuple to multiple variables, the tuple is unpacked into multiple values and then assigned to the corresponding variables, as shown below.

# Packing
a = 1, 10, 100
print(type(a))  # <class 'tuple'>
print(a)        # (1, 10, 100)

# Unpacking
i, j, k = a
print(i, j, k)  # 1 10 100

If the number of unpacked values does not match the number of variables, Python raises ValueError, and the error message will be either too many values to unpack or not enough values to unpack.

a = 1, 10, 100, 1000
# i, j, k = a          # ValueError: too many values to unpack
# i, j, k, l, m, n = a # ValueError: not enough values to unpack

There is one way to solve the problem when the number of variables is smaller than the number of elements: use a starred expression. With a starred expression, one variable can receive multiple values, as shown below. Two points need attention: first, a variable marked with * becomes a list, and that list can contain 0 or more elements; second, only one starred expression can appear in one unpacking statement.

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 []

One more thing should be explained: unpacking syntax works for all sequences. This means the lists we talked about before, range sequences built by the range function, and even strings can all use unpacking syntax. Try running the code below and see what result you get.

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 Variables

Swapping the values of variables is a very common operation when writing code. But in many programming languages, swapping the values of two variables requires an intermediate variable. If you do not use an intermediate variable, then you need to use rather obscure bitwise operations. In Python, swapping the values of variables a and b only needs the code below.

a, b = b, a

Similarly, if you want to rotate the values of three variables a, b, and c, so that the value of b is assigned to a, the value of c is assigned to b, and the value of a is assigned to c, it can be done in the same way.

a, b, c = b, c, a

It should be explained that the operations above do not use packing and unpacking syntax. Python bytecode has instructions such as ROT_TWO and ROT_THREE that can do this operation directly, so the efficiency is very high. But if the values of more than three variables need to be rotated in order, then there is no directly usable bytecode instruction, and we have to complete the swap through packing and unpacking.

Tuples vs Lists

There is another question here that is worth discussing. Python already has the list type, so why do we still need tuples? This question may seem a little difficult for beginners, but it does not matter. Let us first put forward the idea, and everyone can keep learning and understand it gradually.

  1. Tuples are immutable, and immutable types are more suitable for multi-threaded environments, because they reduce the synchronization cost of concurrent access to variables. We will discuss this point later when we talk about concurrent programming.
  2. Tuples are immutable, and usually immutable types are faster to create than the corresponding mutable types. We can use the timeit function in the timeit module to see how much time it takes to create a tuple and a list that hold the same elements. The number parameter of the timeit function means how many times the code is executed. In the code below, we create a list and a tuple that both store the integers from 1 to 9, and each operation runs 10000000 times.
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 result of the code above will be different on different hardware and software systems. On the computer I am using now, creating the list 10000000 times takes 0.635 seconds, while creating the tuple 10000000 times takes 0.078 seconds. Clearly, creating a tuple is faster, and the time difference is about one order of magnitude. You can run this code on your own computer and compare the results.

Of course, tuples and lists in Python can be converted into each other. We can do that with the code below.

infos = ('Luo Hao', 45, True, 'Chengdu, Sichuan')
# Convert the tuple to a list.
print(list(infos))  # ['Luo Hao', 45, True, 'Chengdu, Sichuan']

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

Summary

Lists and tuples are both container-type data types, which means one variable can store multiple pieces of data, 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 do not work for tuples. Lists and tuples both support concatenation, membership operations, indexing, and slicing. The string type that we will talk about later also supports these operations, because a string is also a sequence made up of characters in a certain order. In this respect, there is no difference between the three. We recommend that everyone use list comprehension syntax to create lists. It is not only easy to use, but also very efficient, and it is one of the very distinctive pieces of syntax in Python.