A *tuple* is almost identical to a string in basic structure, except that it is composed of arbitrary components instead of characters. The quotes cannot be used to delimit a tuple because a string can be a component, so a tuple is generally enclosed in parentheses. The following are tuples:

tupl = (2, 3, 5, 7, 11, 13, 17, 19) # Prime numbers under 20

tup2 = (“Hydrogen”,”Helium”,”Lithium”,”Beryllium”,”Boron”, “Carbon”)

tup3 = “hi”, “ohio”, “salut”

If there is only one element in a tuple, there should be a comma at the end:

tup4 = (“one”,)

tup5 = “two”,

That’s because it would not be possible otherwise to tell the difference between a tuple and a string enclosed in parentheses. Is (1) a tuple? Or is it simply the number 1?

A tuple can be empty:

tup = ()

Because they are like strings, each element in a tuple has an index, and they begin at 0. Tuples can be indexed and sliced, just like strings.

tup1[2:4] is (5, 7)

Concatenation is like that of strings, too:

tup4 = tup4 + tup5 # yields tup4 = (‘one’, ‘two’)

As is the case with strings, the index -1 gives the last value in the tuple, -2 gives the second last, and so on. In the example above, tup2[-1] is “Carbon.” Also, like strings, the tuple type is immutable; this means that elements in the tuple cannot be altered. Thus, statements such as

tup1[2] = 6

tup3[1:] “bonjour”

are not allowed and will generate an error.

Tuples are an intermediate form between strings and lists. They are simpler to implement than *list *(which is *lightweight*) and are more general than strings.

Are tuples useful? Yes, it turns out, and part of their use is that they underlie other aspects of Python.

### 1. Tuples in For Loops

Sequences can be used in a for loop to control the iteration and assign the loop control variable. Tuples are interesting in this context because they can consist of strings, integers, or floats. The loop

for i in (“Hydrogen”,”Helium”,”Lithium”,”Beryllium”, “Boron”,”Carbon”):

will iterate 6 times, and the variable i takes on the values in the tuple in the order specified. The variable i is a string in this case. In cases where the types in the tuple are mixed, the situation becomes more complicated.

**Problem**: Print the number of neutrons in an atomic nucleus.

Consider the tuple:

atoms=(“Hydrogen”,1,”Helium”,2,”Lithium”,3,”Beryllium”,4,”Boron”,5,”Carbon”,6)

and the loop

for i in atoms: print (i)

This prints the following:

Hydrogen

1

Helium

2

Lithium

3

Beryllium

4

Boron

5

Carbon

6

The number following the name of the element is the atomic number of that element, the number of protons in the nucleus. In this case, the type of the variable i alternates between string and integer. For elements with a low atomic number (less than 21), a good guess for the number of neutrons in the nucleus is twice the number of protons. The problem is that some of the components are strings and some are integers. The program should only do the calculation when it is in an iteration having an integer value for the loop variable, because a string cannot be multiplied by two.

A built-in function that can be of assistance is isinstance. It takes a variable and a type name and returns True if the variable is of that type and False otherwise. Using this function, here is a program that makes the neutron guess:

atoms=(“Hydrogen”,1,”Helium”,2,”Lithium”,3,”Beryllium”,4,”Boron”,5,”Carbon”,6)

for i in atoms:

if isinstance(i, int):

j = i*2

print (“has “, i, “protons and “, j, ” neutrons.”)

else:

print (“Element “, i)

In other words, in iterations where i is an integer as determined by isinstance, then i can legally be multiplied by 2 and the guess about the number of neutrons can be printed.

Another way to solve the same problem is to index the elements of the tuple. Elements 0, 2, and 4 (even indices) refer to element names, while the others refer to atomic numbers. This code is as follows:

atoms=(“Hydrogen”,1,”Helium”,2,”Lithium”,3,”Beryllium”,4, “Boron”,5,”Carbon”,6)

for i in range(0,len(atoms)):

if i%2 == 1:

j = atoms[i]*2

print (“has “, atoms[i], “protons and “, j,” neutrons.”)

else:

print (“Element “, atoms[i])

Note that in this case, the loop variable is always integer, and is not an element of the tuple but is an index at which to find an element. That’s why the expression atoms[i] is used inside the loop instead of simply i as before.

### 2. Membership

Tuples are not sets in the mathematical sense, because an element can belong to a tuple more than once, and there is an order to the elements. However, some set operations could be implemented using tuples by looking at individual elements (set union and intersection, for example). The intersection of two sets A and B is the set of elements that are members of A and also members of B. The membership operator for tuples is the key word in:

If 1 is in tuplel, the intersection of A and B, where A and B are tuples, is found using the following code:

for i in A:

if i in B:

C = C + i

The tuple C is the intersection of A and B. It works by taking each known element of A and testing to see if it is a member of B; if so, it is added to C.

**Problem**: What even numbers less than or equal to 100 are also perfect squares?

This could be expressed as a set intersection problem. The set of even numbers less than 100 could be enumerated (this is not actual code):

A = 2,4,6,8,10 … and so on

Similarly, the perfect squares could be enumerated,

A = () # Start with an empty tuple

for i in range(0,51): # for appropriate integers

A = A + (i*2,) # add the next even number

# to the tuple

# Can’t simply use A+i because i is integer, not a tuple.

Similarly, the perfect squares could be enumerated,

B = (4,9,16,25,36,49,64,81,100)

or, again, created in a loop:

B = ()

for i in range(0,11):

B = B + ((i*i),)

Now set A can be examined, element by element, to see which members also belong to B:

c = ()

for i in A:

if i in B:

C = C + (i,)

The result is (0, 4, 16, 36, 64, 100).

Two important lessons are learned from this example. First, when constructing a new tuple from components, one can begin with an empty tuple. Second, individual components can be added to a tuple using the concatenation operator +, but the element should be made into a tuple with one component before doing the concatenation.

### 3. Delete

A tuple is *immutable*, meaning that it cannot be altered. Individual elements can be indexed but not changed or deleted. What can be done is to create a new tuple that has new elements; in particular, deleting an element means creating a new tuple that has all of the other elements except the one being deleted.

**Problem**: Delete the element lithium from the tuple atoms, along with its atomic number.

Going back to the tuple atoms, deleting one of the components – in particular, *Lithium* – begins with determining which component *Lithium* is; that is, what is its index? Start at the first element of the tuple and look for the string *Lithium*, stopping when it is found.

for i in range(0, len(atoms)):

if atoms[i] == “Lithium”: # Found it at location i

break;

else:

i = -1 # not found

Knowing the index of the element to be deleted, it is also known that all elements before that one belong to the new tuple and all elements after it do, too. The elements before element i can be written as atoms[0:i]. Each element consists of a string and an integer, and assuming that both are to be deleted means that the elements following element i are atoms[i+2:]. In general, to delete one element, the second half would be atoms[i+1:]. The end of the code snippet for deleting *Lithium* is as follows:

if i>=0:

atoms = atoms[0:i] + atoms[i+2:]

The tuple atoms has not been altered so much as it has been replaced completely with a new tuple that has no *Lithium* component.

### 4. Update

Again, because a tuple is *immutable*, individual elements cannot be changed. A new tuple can be created that has new elements; in particular, updating an element means creating a new tuple that has all of the other elements except the one being updated, and that includes the new value in the correct position.

**Problem**: Change the entry for *Lithium* to an entry for *Oxygen*.

An update is usually a deletion followed by the insertion or addition of a new component. A deletion was done in the previous section, so what remains is to add a new component where the old one was deleted. Inserting the element Oxygen in place of Lithium would begin in the same way as the simple deletion already implemented:

for i in range(0, len(atoms)):

if atoms[i] == “Lithium”: # Found it at location i

break;

else:

i = -1 # not found

Next, a new tuple for Oxygen is created:

newtuple = (“Oxygen”, 8)

And finally, this new tuple is placed at location i while Lithium is removed:

if i>=0:

atoms = atoms[0:i] + newtuple + atoms[i+2:]

However, an update may not always involve a deletion. If *Lithium* is not a component of the tuple atoms, then perhaps *Oxygen* should be added to atoms anyway. Where? How about at the end?

else: # If i is -1 then the new tuple goes at the end

atoms = atoms + newtuple

### 5. Tuple Assignment

One of the unique aspects of Python is the *tuple assignment*. When a tuple is assigned to a variable, the components are converted into an internal form (that is, the one tuples always use). This is called tuple *packing*:

atoms=(“Hydrogen”,1,”Helium”,2,”Lithium”,3,”Beryllium”,4,“Boron”,5,”Carbon”,6)

What is really interesting is that tuple unpacking can also be used. Consider the tuple:

srec = (‘Parker’, ‘Jim’, 1980, ‘Math 550’, ‘C+’, ‘Cpsc 302′,’A+’)

which is a tuple packing of a student record. It can be unpacked into individual variables in the following way:

(fname, lname, year, cmin, gmin, cmax, gmax) = srec

Which is the same as

fname = srec[0]

lname = srec[1]

year = srec[2]

cmin = srec[4]

gmin = srec[5]

cmax = srec[6]

gmax = srec[7]

Of course, the implication is that N variables can be assigned the value of N expressions or variables simultaneously if both are written as tuples. Examples are as follows:

(a, b, c, d, e) = (1,2,3,4,5)

(f, g, h, i, j) = (a, b, c, d, e)

The expression

(f, g, h, i, j) = 2 ** (a,b,c,d,e)

is invalid because the left side of ** is not a tuple, and Python won’t convert 2 into a tuple. Also,

(f, g, h, i, j) = (2,2,2,2,2) ** (a,b,c,d,e)

is invalid because ** is not defined on tuples, nor are other arithmetic operations. As with strings, + means concatenation, though, so (1,2,3) + (4,5,6) yields (1,2,3,4,5,6).

Exchanging values between two variables is a common thing to do. It’s an essential part of a sorting program, for example. The exchange in many languages requires three statements because a temporary copy of one of the variables has to be made during the swap:

temp = a

a = b

b = temp

Because of the way that tuples are implemented, this can be performed in one tuple assignment:

(a,b) = (b,a)

This is a little obscure, even to experienced programmers. A Java programmer could see what was meant, but initially, the rationale would not be obvious. This statement deserves a comment such as “perform an exchange of values using a tuple assignment.”

### 6. Built-in Functions for Tuples

As examples for the table below, use the following:

T1 = (1,2,3,4,5)

T2 = (-1,2,4,5,7)

In addition, tuples can be compared using the same operators as for integers and strings. The comparison is done on an element-by-element basis, just as it is with strings. In the example above, T1>T2 because at the first location where the two tuples differ (the initial component) in the element in T1 is greater than the corresponding element in T2. It is necessary for the corresponding elements of the tuple to be comparable; that is, they need to be of the same type. So if the tuples t1 and t2 are defined as

t1 = (1, 2, 3, “4”, “5”)

t2 = (-1,2,4,5,7)

then the expression t1>t2 is not allowed. A string cannot be compared against an integer, and element 3 of t1 is a string, whereas element 3 of t2 is an integer.

Source: Parker James R. (2021), *Python: An Introduction to Programming*, Mercury Learning and Information; Second edition.