# Tuples

The last data structure we will explore is the tuple. A tuple in mathematics is defined as a “finite ordered list” of items. So, it is a list of items that is not infinite, and the ordering of the items in that list matters.

Thankfully, Python includes support for immutable tuples directly in the language, so we can use them in our programs quickly and easily.

Tuples are generally used when:

• the number of entries in the container is fixed
• the values in the tuple will never change (tuples like strings are immutable)
• the programmer wants indexes which are continuous integers
• integer indexes may will not have gaps– if indexes 1,2 and 7 all have values then indexes 0, 3-6 exists but are assigned `None`
• programmer cares what order Python stores the elements .. elements appear in a for-loop in index order

## Creating a Tuple

To create a tuple in Python, we can simply assign multiple values to a variable, separated by commas:

``tuple_one = 123, 456, 789``

In Python, assigning values to a tuple in this way is referred to as packing. As with lists and dictionaries, we can even store different types of data in the same tuple, as seen below:

``tuple_two = 876, "Hello", True``

Tuples can be nested, using parentheses to enclose any inner tuples for clarity:

``tuple_three = 123, ("Hello", True)``

To create a tuple containing fewer than two elements, we must use some special syntax:

``````# empty tuple uses empty parentheses
tuple_empty = ()

# single item tuple uses a comma after the item
tuple_single = "Hello",  # alterante t_single = ("Hello")``````

## Using a Tuple

To access individual elements of a tuple, we can use the same square bracket notation `[]` we’ve learned with arrays:

``````tuple_access = 123, 456, "Hello"

print(tuple_access)  # 123
print(tuple_access)  # 456
print(tuple_access)  # Hello``````

We can also unpack values in a tuple, storing them in individual variables:

``````tuple_unpack = 123, 456, "Hello"

x, y, message = tuple_unpack

print(x)       # 123
print(y)       # 456
print(message) # "Hello"``````

We can even do the same for a method that returns a tuple:

``x, y = get_coordinates();``

As we can see, tuples appear to be very similar to lists in Python. However, tuples have one important property that sets them apart from lists—they are immutable. This means that once a tuple is created, the items it contains cannot be changed or updated:

``````tuple_modify = 123, 456, "Hello"

tuple_modify = 789  # raises TypeError``````

So, we should always remember that items in a tuple cannot be modified, whereas a list can be modified. Because of that, we typically use tuples when returning data from a function or method, or when storing associated data in a list.

## Example

To really explore how we could use a tuple in our code, let’s build a simple program. Here’s a problem statement:

Write a program that will read lines of input from the terminal. Valid input is a single line with two space separated integers between 0-9.

The program should implement a simple search game. Players will guess coordinates on a 10x10 grid, trying to find a hidden item. So, input will take the form of two integers, separated by a space. If the program is unable to parse an input for any reason, it should print “Invalid Input!” and terminate the program.

When a player makes a guess, the program should respond with one of the four cardinal directions: “North”, “South”, “East”, “West”, indicating the direction of the hidden item from the guess. For simplicity, the program will only respond “North” or “South” if the player’s guess is not on the same row, or `x` coordinate, as the location, and “East” or “West” only if the player’s guess is on the same row as the location. If the player guesses correctly, simply print “Found!” and terminate the program. If the player repeats a guess, the program should print “Repeat!” instead of a cardinal direction.

The hidden location should be randomly generated and stored in a tuple as a global variable, and the program should maintain a global list of tuples representing the locations already guessed.

The program should be implemented as two functions: `main()` that handles reading input and printing output, and a `make_guess()` function that accepts two integers, `x` and `y` as parameters, and responds with a string representing the result of that guess.

Let’s see if we can build this program using tuples.

## `main` Method

First, let’s look at the main function. Once again, we’ll start with skeleton code that is very similar to the other examples in this chapter:

``````import sys
import random

class TupleExample:

guesses = []
location = None

@staticmethod
def main(args):
lines = "0 0"
while len(lines.strip()) > 0:
if len(lines.strip()) > 0:
try:
val = lines.split(" ")
x = int(val)
y = int(val)
output = TupleExample.make_guess(x, y)
print(output)
if output == "Found!":
return

except Exception as e:
print("Invalid Input!")
return

def make_guess(x, y):
# MORE CODE GOES HERE

# main guard
if __name__ == "__main__":
TupleExample.main(sys.argv)``````

At the top of the class, we have included two fields to store our list of previous guesses and the location that should be guessed. For now, we’ll set that value to `None` until we initialize it in our `make_guess()` function.

In this code, we are simply reading a line of input, splitting it into two tokens, and then parsing each token to an integer to get the `x` and `y` values of the guess. After that, we can call the `make_guess()` function with those values, and print the result. Finally, we must check to see if the result is `"Found!"`. If so, we can terminate the program using the `return` keyword.

## `make_guess` Function

Note: video unnecessarily marks the method `make guess` as a static method. We will follow the convention that only the start-up 'main' method shall be static.

Now that we have our `main()` Method written, let’s work on the `make_guess()` function. For starters, we must make sure our random location has been established. So, we’ll do that in the code shown below:

``````def make_guess(x, y):
if TupleExample.location is None:
TupleExample.location = random.randint(0, 9), random.randint(0, 9)``````

Here, we are using the `randint()` method of the `random` library to generate a number between `0` and `9`, inclusive.

Now we must handle producing the output. First, we need to create a new tuple for the guess, and then check and see if the guess is correct, or if it has already been guessed:

``````def make_guess(x, y):
if TupleExample.location is None:
TupleExample.location = random.randint(0, 9), random.randint(0, 9)
guess = x, y
if guess == TupleExample.location:
return "Found!"
if guess in TupleExample.guesses:
return "Repeat!"``````

Here, we are taking advantage of the fact that Python can automatically handle checking for equality between tuples as easily as any other value. As long as both tuples contain the same values in the same order, they are considered equal.

If we find out that the guess is not the hidden location, then we should store it in our list of guesses:

``````def make_guess(x, y):
if TupleExample.location is None:
TupleExample.location = random.randint(0, 9), random.randint(0, 9)
guess = x, y
if guess == TupleExample.location:
return "Found!"
if guess in TupleExample.guesses:
return "Repeat!"
TupleExample.guesses.append(guess)``````

Finally, we can handle printing the hints for cases where the guess is not on the same row:

``````def make_guess(x, y):
if TupleExample.location is None:
TupleExample.location = random.randint(0, 9), random.randint(0, 9)
guess = x, y
if guess == TupleExample.location:
return "Found!"
if guess in TupleExample.guesses:
return "Repeat!"
TupleExample.guesses.append(guess)
if guess > TupleExample.location:
return "North"
if guess < TupleExample.location:
return "South"``````

Below that, we can assume that the guess is on the same row, so we’ll have to handle the cases where the guess is east or west of the location:

``````def make_guess(x, y):
if TupleExample.location is None:
TupleExample.location = random.randint(0, 9), random.randint(0, 9)
guess = x, y
if guess == TupleExample.location:
return "Found!"
if guess in TupleExample.guesses:
return "Repeat!"
TupleExample.guesses.append(guess)
if guess > TupleExample.location:
return "North"
if guess < TupleExample.location:
return "South"
if guess > TupleExample.location:
return "West"
else:
return "East"``````

There we go! That method will handle producing the output for any guess provided by the user. See if you can complete the code in `TupleExample.py` to the left, then use the two assessments below to check your program.