# Python tuples

## Contents

# Python tuples¶

A sequence is a bunch of ordered objects. Ordered in the sense that you can say, “this is first,” and “this is second.” Obviously, sequences are very useful for data science. Data are made out of sequences of stuff.

There are two ways you can have sequences in Python.
You can either have `tuple`

’s or … `list`

’s.
The difference between the two is that `tuple`

are frozen (you cannot change them once you make them) but you can add or remove items for `list`

’s.
Let’s look at tuples first.

Right now, you only know of `int`

’s, `float`

’s, `bool`

’s and `NoneType`

.
So, that’s all I will use to make tuples.
But you can put anything in a tuple.
Here is a tuple with two integers:

```
x = (1, 2)
x
```

```
(1, 2)
```

Here is a tuple with two floats:

```
y = (2.1232, 3.14)
y
```

```
(2.1232, 3.14)
```

Here is a tuple with two integers and a float:

```
z = (5, 0, 34.34)
z
```

```
(5, 0, 34.34)
```

All, these are tuples:

```
type(x)
```

```
tuple
```

```
type(y)
```

```
tuple
```

```
type(z)
```

```
tuple
```

You can ask a `tuple`

how many elements it has using the function `len`

:

```
len(x)
```

```
2
```

```
len(y)
```

```
2
```

```
len(z)
```

```
3
```

## Indexing of tuples¶

You can access any element of a tuple using **indexing**.
Note that indices start at 0 and go up to the `len()`

of the tuple:

```
z[0]
```

```
5
```

```
z[1]
```

```
0
```

```
z[2]
```

```
34.34
```

If you try to try to use an index larger than the length of the tuple, here is what happens:

```
z[3]
```

```
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-13-a849b2246650> in <module>
----> 1 z[3]
IndexError: tuple index out of range
```

You get an `IndexError`

. This is a very common bug. You need to learn how to read it.

You can also use negative indices:

```
z[-1]
```

```
34.34
```

Negative indices give you the elements starting from the last one.
In particular `z[-1]`

gives you the last element of the tuple.
What would you get with `z[-2]`

:

```
z[-2]
```

```
0
```

```
z[-3]
```

```
5
```

So, `z[-3]`

gives you the third element of the tuple counting from the last.
Because `z`

has just three elements, `z[-3]`

is the same as `z[0]`

.
What would happen if you tried `z[-4]`

?

```
z[-4]
```

```
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-17-e93807d81ae4> in <module>
----> 1 z[-4]
IndexError: tuple index out of range
```

You get an `IndexError`

again.

Okay. What if you wanted to get the first and second elements of `z`

and not the last one. You can do this using **index ranges**:

```
z[0:2]
```

```
(5, 0)
```

How about the second and the third:

```
z[1:3]
```

```
(0, 34.34)
```

Let’s make a bigger tuple so that we can do some more indexing:

```
w = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
```

This has 10 elements:

```
len(w)
```

```
10
```

To get the first five you can do:

```
w[0:5]
```

```
(1, 2, 3, 4, 5)
```

Now, if the first index in your range happens to be zero, then you can skip it.
So, instead of `w[0:5]`

, you can just write:

```
w[:5]
```

```
(1, 2, 3, 4, 5)
```

Let’s get the last 3:

```
w[7:10]
```

```
(8, 9, 10)
```

Note that this gives you `w[7], w[8], w[9]`

.
Just like before, if you want something like `w[7:10]`

you can instead write:

```
w[7:]
```

```
(8, 9, 10)
```

So when you ask for `w[i:j]`

you get all elements between `w[i]`

and `w[j-1]`

.
Let’s see what you will get when you do this:

```
w[4:5]
```

```
(5,)
```

This just gave you a tuple with just the fifth element.
At this point, look at how a tuple with a single element is defined.
It is `(5,)`

instead of `(5)`

. The comma is essential.
This is because without the comma `(5)`

is just five …

```
type((5))
```

```
int
```

```
type((5,))
```

```
tuple
```

```
(5)
```

```
5
```

```
(5,)
```

```
(5,)
```

```
len((5,))
```

```
1
```

How can I have a tuple with zero elements? Can you guess?
Well, it is just `()`

:

```
()
```

```
()
```

```
type(())
```

```
tuple
```

```
len(())
```

```
0
```

Here is another way to get a tuple with zero elements:

```
w[5:5]
```

```
()
```

```
len(w[5:5])
```

```
0
```

Alright, now some more advanced indexing. You do not have to remember all these. I am just putting them here for your reference.

First, you can have ranges with negative numbers. This is how to get everything from the fourth element counting from the end to the second element counting from the end:

```
w[-4:-2]
```

```
(7, 8)
```

And if you want to go all the way to the last element you can write:

```
w[-4:10]
```

```
(7, 8, 9, 10)
```

Or you can completely skip the last element:

```
w[-4:]
```

```
(7, 8, 9, 10)
```

Meditate a little bit with this example with negative indices:

```
w[-4:-1]
```

```
(7, 8, 9)
```

This means that `w[-i:-j]`

gives everything between `w[len(w)-i]`

and `w[len(w)-j]`

for `i`

and `j`

positive numbers in this example.

What happens if you try to give a range that doesn’t make sense. Like, take me all the way from the 5th element to the 2nd element:

```
w[5:2]
```

```
()
```

You get an empty tuple. Nice. What happens if you mix positive and negative indices:

```
w[-6:8]
```

```
(5, 6, 7, 8)
```

It works just fine if the ranges make sense. Of course, avoid this because it is quite confusing.

Indexing is getting quite long, but let me give you one more example before we move on. Here is how you can take skip elements in between:

```
w[0:10:2]
```

```
(1, 3, 5, 7, 9)
```

```
w[1:5:2]
```

```
(2, 4)
```

So, `w[i:j:k]`

will give you every k-th element from `i`

(inclusive) to `j`

(exclusive).
For \(k=1\), of course, you get everything:

```
w[1:5:1]
```

```
(2, 3, 4, 5)
```

Let’s say now that we want to take every other element as we did above. We can do this:

```
w[0:10:2]
```

```
(1, 3, 5, 7, 9)
```

But because we start at zero, we can skip the first index:

```
w[:10:2]
```

```
(1, 3, 5, 7, 9)
```

And because we end at `len(w)`

(which is 10), we can also skip the second index:

```
w[::2]
```

```
(1, 3, 5, 7, 9)
```

Similarly, if we wanted to take every third element we would do:

```
w[::3]
```

```
(1, 4, 7, 10)
```

You can also make `k`

negative. Let’s see what happens:

```
w[::-1]
```

```
(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
```

Wow, the elements are reveresed. How about this:

```
w[::-2]
```

```
(10, 8, 6, 4, 2)
```

Reveresed and we skip every other element.
Note that `w[::-2]`

is equivalent to:

```
w[10:0:-2]
```

```
(10, 8, 6, 4, 2)
```

So, the range has to make sense. You say, I want to go from the 10th element to the first one backwards skipping every other one.

## Concatentating tuples¶

You can concatenate tuples. Here is how:

```
x + y
```

```
(1, 2, 2.1232, 3.14)
```

```
x + y + z
```

```
(1, 2, 2.1232, 3.14, 5, 0, 34.34)
```

Nice. Observe that `x + y`

and `y + x`

are different:

```
x + y
```

```
(1, 2, 2.1232, 3.14)
```

```
y + x
```

```
(2.1232, 3.14, 1, 2)
```

What if you add the `()`

(0-element tuple) to a tuple:

```
x + ()
```

```
(1, 2)
```

```
() + x
```

```
(1, 2)
```

You just get the tuple back.

## You cannot change tuples¶

You cannot change a tuple once you create it. For example, you cannot do this:

```
w[4] = 5
```

```
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-59-6b006644fcad> in <module>
----> 1 w[4] = 5
TypeError: 'tuple' object does not support item assignment
```

Read the error message. It is quite clear: “TypeError: ‘tuple’ object does not support item assignment.” Always read the error messages.

So, you should not be using `tuple`

’s for things that may change.
You should only use `tuple`

’s for things that do not change.
As a matter of fact, **you should always use tuples’s for things that do not change**.
In this way, if you try to change that object you will get this nice error above instead of a nasty bug.

If you want something that can be changed, then you need to use a `list`

.

## Questions¶

In the code block provided below evaluate the angle between the two vectors: $\( \vec{r}_1 = 4\hat{i} + 3.5\hat{j} + 2.5\hat{k}, \)\( and \)\( \vec{r}_2 = 1.5\hat{i} + 2.5\hat{j}. \)\( Remember that the dot product of two vectors is: \)\( \theta = \cos^{-1}\left(\frac{\vec{r}_1\cdot \vec{r}_2}{|\vec{r}_1||\vec{r}_2|}\right). \)$ Hint: Use tuples to represent

`r1`

and`r2`

. You will have to`import math`

and use`math.acos()`

.

```
# Your code here
```

Consider the

`tuple`

:

```
q = (3.0, 23.0, 1e-3, 12.242, 23, 41, 50, 30., 20.)
```

Do the following:

Get the lentgh of q:

Get all the elements of

`q`

from the second to the second to last

Get every other element of

`q`

from the second to the second to last

Reverse

`q`