Python Basics
Collections - Tuples
Tuples are immutable sequences, typically used to store collections of heterogeneous data. Tuples are also used for cases where an immutable sequence of homogeneous data is needed.
Tristan
contact-datartichaut@pm.me
Python projects list
Libraries.
import copy
from collections import namedtuple
Beginner
What are tuples and why use them.
In more technical termes, tuples are defined as :
Tuples being immutable, it helps writing "safer" code. At any given time you know that the content of your tuple is what you set it to be at the beginning, so if you don't need to add other elements to your collection, tuples are great.
Another advantage is that tuple are faster to work with than lists, although this might not often be a good enough reason to choose one over the other.
ToC
t1 = tuple()
t1 = ()
They can be initialized directly with values.
# A basic tuple.
t1 = (1, 2, 3)
# A tuple can contain different types of data.
t1 = (1, 'word', '2', True)
# Singleton.
t1 = (1,)
A tuple cannot be created with a single value with t1 = (1)
This syntax is not possible because the parenthesis are already used to group statements.
We can add a coma after the lone element to say explicitly that we whant to create a tuple.
Beginner
Accessing elements in a tuple.
Just like lists there are several ways to access elemnts in a tuple.
Tupes being immutables, it is worth noting that accessing an element of a tuple doesn't allow to modify it.
ToC
# A basic tuple.
t1 = (1, 2, 3)
Accessing a specific value by index using the []
notation.
val = t1[0]
print(val)
Getting the index of a specific value with the tuple.index
method.
_ = t1.index(1)
Everything related to slicing that works on lists works for tuples too.
Beginner
Operations on tuple.
Mathematical operations are supported and can help concatenate or reproduce tuples, for example.
ToC
Additions.
t1 = (1, 2) + (3, 4)
print(t1)
Product.
t1 = (1,) * 5
print(t1)
Special assignment operators.
t1 += (2,)
print(t1)
Beginner
Adding, modifying and deleting elements in a tuple.
Tuples being immutable, it is not possible to add or delete elements nor is it possible to modify a value already in the tuple.
A workaround can be to create a new tuple as a subset of an old tuple, for example.
ToC
Beginner
Copying a tuple.
Tuples are immutable and as such, copying does not work the same as for lists or other mutable collection
Furthermore, tuples can contain mutable object which add a layer of complexity.
The basic idea is : since a tuple is immutable, we do not need to copy it. If you need a second variable to hold the same values as another already existing tuple, a simple reference to this same tuple should suffice.
But since tuples can contain mutable object, we still need to be able to copy it in some occasions.
ToC
Creating a copy that works in every situation without thinking about it.
# A basic tuple ...
t1 = (1, 2)
# ... copied to another variable.
t2 = copy.deepcopy(t1)
The copy.deepcopy
function is the simplest way to create a copy if you do not
want to delve too much into how python handle copying immutable objects.
Intermediate
Let's look at a few examples. First with a basic tuple.
t1 = (1, 2)
t2 = t1
t3 = copy.copy(t1)
t4 = copy.deepcopy(t1)
In this case, none of t2
, t3
or t4
are "real" copies of
t1
. They are all references to the first tuple.
One could expect that deepcopy would have created a real copy but t1
being immutable containing
only immutable objects (integers are immutable objects), making a copy of t1
"does not make sense".
Now let's look at a more complex example with a tuple containing a mutable object.
t1 = (1, 2, [3, 4])
t2 = t1
t3 = copy.copy(t1)
t4 = copy.deepcopy(t1)
Unlike with a basic tuple, t3
is an actual copy (and it is the only real copy).
We can verify that by modifying one tuple and observing the effect on the others.
t1[2][0] = 10
print(f"t1 : {t1}")
print(f"t2 : {t2}")
print(f"t3 : {t3}")
print(f"t4 : {t4}")
Advanced
We can illustrate that by accessing the memories addresses of these objects.
print(f"t1 : {hex(id(t1))}")
print(f"t2 : {hex(id(t2))}")
print(f"t3 : {hex(id(t3))}")
print(f"t4 : {hex(id(t4))}")
One question remain. If a tuple is immutable, how can it be modified and still be the same object ?
print(f"t1 before modification : {hex(id(t1))}")
t1[2][0] = 100
print(f"t1 before modification : {hex(id(t1))}")
It can be that way because the list inside of t1
is mutable. It means that it can be modified
without having to create a new object.
t1
does not actually contain the list itself but rather a reference to this link so even
if elements in the list are changed, the reference is still the same and t1
is still the same
object.
# A simple tuple.
t1 = (10, 12, 31)
# Iterating directly on the tuple.
for elem in t1:
print(elem)
# Using the enumerate function.
for ind, elem in enumerate(t1, start=1):
print(f'{elem} is the {ind}{"st" if ind == 1 else "nd" if ind == 2 else "th"} element of the tuple.')
Beginner
Logic tests with tuples.
Just like with lists, a tuple can be tested and is Truthy if not empty.
Since tuples are immutable, they cannot be used in ToCwhile
loops by removing elements
each iterations. They can stil be used in if
statements.
t1 = (1, 2)
if t1:
print('This tuple is not empty.')
# Unpacking a simple tuple.
data = (1, 2, 3)
x, y, z = data
Note that tuple are the default when a function returns multiple values. Its immutable caracteristics makes it the best candidate for the job.
Intermediate
Tuple comprehension.
Tuple comprehension works the same as with list comprehension. The syntax is slightly deferent though.
It is needed to specify "tuple( ... ) ".
ToC
# A simple tuple.
t1 = (1, 2, 3, 4)
# Using tuple comprehension to get every element times two.
t2 = tuple(i * 2 for i in t1)
Using only parenthesis doe not yield a tuple but a generator.
gen = (i * 2 for i in t1)
The next function allows us to iterate through the generator.
next(gen)
next(gen)
list(gen)
A generator does not compute every element when it is created, countrary to list and tuple comprehension.
The generator calculate only the needed element when it is required to.
This makes generator a powerfull tool for iteratinf over a very large number of elements.
Intermediate
Named tuples.
Named tuples assign meaning to each position in a tuple and allow for more readable, self-documenting code. They can be used wherever regular tuples are used, and they add the ability to access fields by name instead of position index.
Let's look at an example.
ToC
Point = namedtuple('Point', ['x', 'y'])
Here we created a named tuple named Point
.
It takes to arguments which are its x and y coordinates.
This allow us to create a new point like so :
p = Point(11, y=22)
print(p)
We can then access its attributes.
print(p[0] + p[1])
print(p.x + p.y)
Namedtuples are great for readability and structure of the code.