5. Tuples#

A tuple is a sequence of values. The values can be any type, and they are indexed by integers, so in that respect tuples are a lot like lists. The important difference is that tuples are immutable.

Syntactically, a tuple is a comma-separated list of values:

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

Although it is not necessary, it is common to enclose tuples in parentheses.

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

To create a tuple with a single element, you have to include a final comma.

t = 'a',
type(t)
tuple

A value in parentheses is not a tuple.

t = ('a')
type(t)
str

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

t = tuple()
print(t)
()

If the argument is a sequence (string, list or tuple), the result is a tuple with the elements of the sequence.

t = tuple('blencathra')
print(t)
('b', 'l', 'e', 'n', 'c', 'a', 't', 'h', 'r', 'a')

Because tuple is the name of a built-in function, you should avoid using it as a variable name. Most list operators also work on tuples. The bracket operator indexes an element.

t = ('a', 'b', 'c', 'd', 'e')
print(t[0])
a

And the slice operator selects a range of elements.

print(t[1:3])
('b', 'c')

But if you try to modify one of the elements of the tuple, you get an error.

t[0] = 'A'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [9], line 1
----> 1 t[0] = 'A'

TypeError: 'tuple' object does not support item assignment

You can’t modify the elements of a tuple, but you can replace one tuple with another.

t = ('A',) + t[1:]
print(t)

It is often useful to swap the values of two variables. With conventional assignments, you have to use a temporary variable. For example, to swap a and b.

a = 1
b = 3
temp = a
a = b
b = temp

This solution is cumbersome, tuple assignment is more elegant.

a, b = b, a

The left side is a tuple of variables; the right side is a tuple of expressions. Each value is assigned to its respective variable.
All the expressions on the right side are evaluated before any of the assignments.

The number of variables on the left and the number of values on the right have to be the same.

More generally, the right side can be any kind of sequence
(string, list or tuple).  For example, to split an email address
into a user name and a domain, you could write.
  File "/tmp/ipykernel_113398/2249272864.py", line 1
    More generally, the right side can be any kind of sequence
                                                   ^
SyntaxError: invalid syntax. Perhaps you forgot a comma?
addr = 'monty@python.org'
uname, domain = addr.split('@')
print(uname)
print(domain)
monty
python.org

5.1. Exercise 1#

Strictly speaking, python functions can only return a single value. Write a function to demonstrate how tuples can be used in this context.

5.2. Solution 1#

def line(p1,p2) :
    a,b = p1
    c,d = p2
    return ((d-b)/(c-a),(d*(c-a)-b*(d-b))/(c-a))
print(line((1,2),(5,8)))
(1.5, 5.0)

5.3. Exercise 2#

What would you think if someone said a python function can only take a single argument ?

5.4. Gather and scatter#

Functions can take a variable number of arguments. A parameter name that begins with * gathers arguments into a tuple. For example.

def f(*vals) :
    for val in vals :
        print(val)
        
f(1,2,"hello")
1
2
hello

The compliment of gather is scatter. For example.

def g(a,b) :
    return a + b

t = (1,3)
g(*t)

Many of the built-in functions use variable-length argument tuples. For example, max and min can take any number of arguments.

max(1,2,3,2,-1)
max(7,-3)

5.4.1. Exercise 3#

Write a function that takes an arbitary number of numerical values and returns both the maximium and minimum value of the arguments.

5.4.2. Soliution 3#

def maxmin(*vals) :
    return max(vals),min(vals)

maxmin(1,2,3,2,5,0,-9)

5.5. Lists and tuples#

zip is a built-in function that takes two or more sequences and “zips” them into a iterator of tuples where each tuple contains one element from each sequence.

x = [1,2,3,4]
y = ["a","b","c","d"]
z = zip(x,y)
print(z)
<zip object at 0x7f8a65ae1380>

The iterator can be converted to a list using list.

list(z)
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]

An iterator can be used in a loop directly though.

for val in z :
    print(val)

Notice though that, as it stands, the above code does not work. That is because the iterator has already been consumed by the list function.

z = zip(x,y)
for val in z :
    print(val)
(1, 'a')
(2, 'b')
(3, 'c')
(4, 'd')

zip can be useful in conjunction with list comprehensions.

5.5.1. Exercise 4#

What would you expect the following code to do ?

X = [1,2,3,4]
Y = [5,6,7,8]
Z = [9,10,11,12]
S = [x + y + z for x,y,z in zip(X,Y,Z)]
print(S)
[15, 18, 21, 24]