# Python Concepts/Tuples

## Objective

 Learn about Python tuples. Learn about tuple indexing. Learn about tuple slicing. Learn about built-in tuple functions. Learn when to use tuples and when not to.

## Lesson

### The Python Tuple

In Python, a tuple is an immutable sequence. This means that a tuple is similar to a list, except you cannot dynamically modify the tuple itself. Once it's created, it can't be changed or modified. The way the tuple is stored in memory will also be important later in the course. To create a tuple, you create a group of items separated by a comma (`,`). Even though Python adds parentheses around a group of items, it isn't required because it wouldn't change the meaning of the tuple.

```>>> spam = 1, 2, 3
>>> spam
(1, 2, 3)
>>> "A", "B", "C", "D"
('A', 'B', 'C', 'D')
>>> True, False, True, True, False
(True, False, True, True, False)
>>> (1, 3, "G", True)
(1, 3, 'G', True)
```

Now critically think about this question. What if you need only one item stored in your tuple? You can accomplish this by leaving an excess comma at the end. In fact, you can leave an excess comma at the end of any tuple, regardless of size, although it isn't explicitly required for tuples larger than one. This is a little quirk in the Python Language.

```>>> bacon = 1,
>>> bacon
(1,)
>>> spam = 1, 2, 3,
>>> spam
(1, 2, 3)
```

Also note that an empty tuple is simply a pair of parentheses. To review:

```>>> a = () ; a ; isinstance(a, tuple)
()
True
>>> a = (1) ; a ; isinstance(a, tuple)
1
False
>>> a = (1,) ; a ; isinstance(a, tuple)
(1,)
True
>>> a = (1,2) ; a ; isinstance(a, tuple)
(1, 2)
True
>>> a = (1,2,) ; a ; isinstance(a, tuple)
(1, 2)
True
>>>
```

### Tuple Indexing

Tuples, like lists and strings, follow the same standard for indexing. Indexing starts at 0 for the first item and -1 for the start of the last item. Examples are shown below.

```>>> ("A", "B", "C", "D", "E", "F")[0]
'A'
>>> ("A", "B", "C", "D", "E", "F")[1]
'B'
>>> ("A", "B", "C", "D", "E", "F")[-1]
'F'
>>> ("A", "B", "C", "D", "E", "F")[-2]
'E'
```

### Tuple Slicing

Like other common sequences, tuples can be sliced. The standard slicing is exactly like slicing taught in previous lessons:

```>>> ("A", "B", "C", "D", "E", "F")[1:]
('B', 'C', 'D', 'E', 'F')
>>> ("A", "B", "C", "D", "E", "F")[:4]
('A', 'B', 'C', 'D')
>>> ("A", "B", "C", "D", "E", "F")[-4:]
('C', 'D', 'E', 'F')
>>> ("A", "B", "C", "D", "E", "F")[0:]
('A', 'B', 'C', 'D', 'E', 'F')
>>> ("A", "B", "C", "D", "E", "F")[0:2]
('A', 'B')
>>> ("A", "B", "C", "D", "E", "F")[11:]
()
>>> ("A", "B", "C", "D", "E", "F")[:-19]
()
```

Tuples also support extended slicing. Again, the third parameter acts like a step:

```>>> ("A", "B", "C", "D", "E", "F")[::]
('A', 'B', 'C', 'D', 'E', 'F')
>>> ("A", "B", "C", "D", "E", "F")[::1]
('A', 'B', 'C', 'D', 'E', 'F')
>>> ("A", "B", "C", "D", "E", "F")[::2]
('A', 'C', 'E')
>>> ("A", "B", "C", "D", "E", "F")[::3]
('A', 'D')
>>> ("A", "B", "C", "D", "E", "F")[::-1]
('F', 'E', 'D', 'C', 'B', 'A')
>>> ("A", "B", "C", "D", "E", "F")[::-2]
('F', 'D', 'B')
>>> ("A", "B", "C", "D", "E", "F")[:3:-1]
('F', 'E')
>>> ("A", "B", "C", "D", "E", "F")[0:4:1]
('A', 'B', 'C', 'D')
>>> ("A", "B", "C", "D", "E", "F")[-2::-1]
('E', 'D', 'C', 'B', 'A')
>>> ("A", "B", "C", "D", "E", "F")[:-19:-1]
('F', 'E', 'D', 'C', 'B', 'A')
>>> ("A", "B", "C", "D", "E", "F")[::11]
('A',)
>>> ("A", "B", "C", "D", "E", "F")[::-56]
('F',)
```
Note: Extended slicing can be tricky for some people. If you aren't too confident with the concept, review this lesson. This lesson also has some general information about common sequence tricks.

## Built-in Tuple Functions

### `.count()`

Returns the number of times a given item is found.

```>>> votes = ("yes", "no", "yes", "yes", "no")
3
2
0
>>>
```

### `.index()`

Finds the first index of a given item. The two optional parameters are for the start and end of the tuple's index, respectively.

```>>> votes = ("yes", "no", "yes", "yes", "no")
>>> vote.index("no")
1
>>> vote.index("yes")
0
>>> vote.index("yes", 1)
2
>>> vote.index("no", 2)
4
>>> vote.index("yes", 1, 4)
2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: tuple.index(x): x not in tuple
>>>
```

To determine the exact position of a value (including False if not found):

```>>> position = False
...
>>> position
False
...
>>> position
1
>>>
```

## Tuple Usage

 Tuples are great for holding static data that you don't plan on modifying and changing a lot. Like the previous lesson stated, tuples are four to seven times faster than lists. This means for every list item manipulated, four to seven tuples could be manipulated within that time frame. This has huge advantages for scientific work, as this allows for speedy data reading. Although all of the tuple's advantages sound good, the tuple itself is static and immutable. This means that a tuple's content is read-only. To change its content, a tuple must be converted to a dynamic, mutable sequence and then converted back to a tuple. For this reason, if you do a lot of adding and removing of items in a sequence, you shouldn't use the tuple. It is not possible to assign to the individual items of a tuple, however it is possible to create tuples that contain mutable objects, such as lists. Tuples containing lists behave like lists. For example, create a tuple containing a list of five rows in which each row contains four powers of integer ${\displaystyle i:[i,\ i^{2},\ i^{3},\ i^{4}]:}$ ```>>> >>> a = tuple( [ [i, i*i, i*i*i, i*i*i*i] for i in range(-2,3) ] ) ; a ([-2, 4, -8, 16], [-1, 1, -1, 1], [0, 0, 0, 0], [1, 1, 1, 1], [2, 4, 8, 16]) # enclosing parentheses '()'. >>> >>> isinstance(a,tuple) True >>> >>> a[0] ; a[1] ; a[2] ; a[3] ; a[4] [-2, 4, -8, 16] [-1, 1, -1, 1] [0, 0, 0, 0] [1, 1, 1, 1] [2, 4, 8, 16] >>> a[0][3] ; a[1][2] ; a[2][1] ; a[3][0] ; a[4][1] 16 -1 0 1 4 >>> >>> a += ([-5,-4,-3,-2],) ; a # tuple extended by concatenation. Note the comma '],)'. ([-2, 4, -8, 16], [-1, 1, -1, 1], [0, 0, 0, 0], [1, 1, 1, 1], [2, 4, 8, 16], [-5, -4, -3, -2]) >>> >>> a[3][2] = -13 ; a ([-2, 4, -8, 16], [-1, 1, -1, 1], [0, 0, 0, 0], [1, 1, -13, 1], [2, 4, 8, 16]) # Contents of list within a tuple can be manipulated. >>> >>> b = list(a) ; b [[-2, 4, -8, 16], [-1, 1, -1, 1], [0, 0, 0, 0], [1, 1, -13, 1], [2, 4, 8, 16]] # Shallow copy of tuple a. >>> >>> b[2][1] = -7 ; b ; a [[-2, 4, -8, 16], [-1, 1, -1, 1], [0, -7, 0, 0], [1, 1, -13, 1], [2, 4, 8, 16]] # ([-2, 4, -8, 16], [-1, 1, -1, 1], [0, -7, 0, 0], [1, 1, -13, 1], [2, 4, 8, 16]) # Both are affected. >>> >>> a[0] = [3,5,7,9] # Basic structure of tuple cannot be changed. Traceback (most recent call last): File "", line 1, in TypeError: 'tuple' object does not support item assignment >>> ``` For a true deep copy: ```>>> import copy >>> >>> t1 = ([-2, 4, -7, 16], [-1, 1, -1, 1], ) ; t1 ([-2, 4, -7, 16], [-1, 1, -1, 1]) >>> t2 = copy.deepcopy(t1) ; t1 ; t2 ([-2, 4, -7, 16], [-1, 1, -1, 1]) ([-2, 4, -7, 16], [-1, 1, -1, 1]) >>> t1[1][2] = -3 ; t1 ; t2 ([-2, 4, -7, 16], [-1, 1, -3, 1]) # Only t1 has changed. ([-2, 4, -7, 16], [-1, 1, -1, 1]) # t2 remains a true copy of t1 before t1 was changed. >>> ```

## Assignments

 Work with some tuples. Try creating a tuple of tuples. Work with some of the tuple's methods. Try them all out at least three times. Tuples are the fastest sequence in Python. How could they be used in a real life situation?