Welcome back to another article in my Intro to Programming series.
In this post, we'll talk about tuples. If you're already familiar with lists in Python from our previous article in this series, you'll understand tuples in no time. Why?
Because tuples are almost the same as lists, with one key difference: they are immutable. We discussed the concept of immutability when we talked before about strings. As you might recall, immutable items cannot be changed.
With tuples, immutability means three things. First, that items in tuples cannot be changed or reassigned. Second, that we cannot add new items to a tuple once the tuple has been defined. And third, that we cannot delete items from a tuple once a tuple has been defined. We'll see all of these properties in action in just a bit.
In terms of notation, tuples are similar to lists. Where lists used square brackets, tuples use parentheses. To sum up our last few articles, we can recall that we use square brackets for lists, curly brackets for dictionaries, and now, round parentheses for tuples.
Also like lists, tuples can pretty much contain any other data type, including strings, integer numbers, floating-point numbers, lists, dictionaries, and even other tuples. And each of these items can be further nested into as many levels as needed. So just like lists and dictionaries, tuples are flexible in terms of what we can store in them.
Let's take a look at some code. We’ll explore tuples in more detail using a Jupyter notebook. Please feel free to start up your own Jupyter notebook to follow along.
We’ll start with some simple examples. I’ll create a tuple and call it my_tuple. Inside it, I'll write the numbers 1,2,3,4,5 inside parentheses.
Similarly, I’ll also create a list and call it my_list. Inside it, I’ll again write 1,2,3,4,5, but this time between square brackets. We can use the built-in type function to check the type of the two variables. Using type(my_tuple), we see that I created a tuple. And again, using type(my_list) outputs a list. The len method can also be used with both the list and the tuple to return the number of items in each. Inputting len(my_tuple) returns 5, as we’d expect. Inputting len(my_list) also gives us 5.
# let's create a tuple and a list my_tuple = (1,2,3,4,5) my_list = [1,2,3,4,5] # we can check the type of our variables # output should be tuple type(my_tuple) # output should be list type(my_list) # we can also check the length of both # output should be 5 len(my_tuple) # output should be 5 len(my_list)
The print function can also be used with both lists and tuples. You can expect to use this function when writing longer programs, or when writing code outside of Jupyter notebooks. Inputting print(my_tuple) prints the tuple, and print(my_list) prints the list.
# let's print our tuple # output should be (1, 2, 3, 4, 5) print(my_tuple) # and our list # output should be [1, 2, 3, 4, 5] print(my_list)
Like lists and dictionaries, tuples can be empty. In the case of an empty tuple—as you might have guessed by this point in our series—we only write the round brackets: new_tuple = (). We can check the data type of the variable we just named new_tuple using the type function: type(new_tuple). In doing so, Python returns a tuple. We can also check the length using the len function and we can expect to get 0 using len(new_tuple).
# let's create an empty tuple new_tuple = () # let's check the type of new_tuple # output should be tuple type(new_tuple) # let's check the length of new_tuple # output should be 0 len(new_tuple)
One quirk of tuples in Python is that even if they only include a single item, the correct syntax includes a comma after that item. Let’s look at an example. I’ll create the variable one_item_tup, and in it I’ll store a tuple with a single item. In this case I’ll make my single item the string "a". Now, if we check the type of one_item_tup, Python will confirm it's a tuple.
# let's create a one-item tuple # let's make sure to add the comma one_item_tup = ("a",) # and check the type of one_item_tup # output should be tuple type(one_item_tup)
But what happens if we remove the comma from the previous example but retain the parentheses? Is one_item_tup still a tuple? Let's try it out. Using our type function, we see that one_item_tup is now a string. What happened?
# let's see what happens if we create a tuple without the comma one_item_tup = ("a") # when we check the type of one_item_tup... # output should be str type(one_item_tup)
After we removed the comma from inside the tuple, Python no longer considered one_item_tup to be a tuple. Instead, Python sees it as a string, which is the data type of our input "a" in this example. This means that if you want to create a tuple with a single item, simply putting it between parentheses won't be enough. We must include a comma after the item in order for Python to know that it is actually a one-item tuple. Otherwise, Python reads it as just the item itself.
Tuples are also ordered sequences, which means that we can use indexing and slicing in the same way that we did with strings and lists. In our current example, writing my_tuple returns 1, which is the item at index location 0. Remember, we always use the square brackets for accessing an item at a given index location, whether doing so with a tuple or a list. Let's try another example. Writing my_tuple returns 2, which also makes sense. Reverse indexing is also available for tuples. Writing my_tuple[-1] returns 5. If you remember from earlier articles, -1 is the index position of the last item in a list, or the last character in a string.
# we can use indexing to access values in tuples # output should be 1 my_tuple # output should be 2 my_tuple # we can also use reverse indexing # output should be 5 my_tuple[-1]
So far this discussion should sound pretty familiar. Everything we did here we also did when learning about lists. Now let's talk about what makes a tuple different from a list.
Namely, let’s discuss the immutability of tuples. As we said earlier, immutability prevents us from doing three things to a data type: changing an item, adding an item, or deleting an item. We’ll go over these one by one.
Going back to the list we created earlier, I want to change the value we stored at index location 0. Specifically, I want to make that integer number 10 instead of 1. We can easily do that by running my_list = 10. Outputting the list, we see that the item at index 0 is now 10.
# we can change a value in a list my_list = 10 # output should be [10, 2, 3, 4, 5] print(my_list)
Let's try to do that with the tuple we created earlier. I’ll write my_tuple = 10. Running this gives me a TypeError, which clearly informs me that tuples don't support item assignment. In other words, an item, once inside a tuple, cannot be changed.
# we cannot change items inside a tuple my_tuple = 10 # output will be the runtime error below # →--------------------------------------------------------------------------- # TypeError Traceback (most recent call last) # <ipython-input-23-88963aa635fa> in <module>() # ----> 1 my_tuple = 10 # TypeError: 'tuple' object does not support item assignment
With lists, we also previously learned that we can use append to add a new item at the end. For example, let's add the integer 6 to my_list. I’ll do so by running my_list.append(6). If we print my_list now, we see that 6 was added.
# we can add on to a list using append my_list.append(6) #output will be [10, 2, 3, 4, 5, 6] print(my_list)
Now let’s try to use append with the tuple by writing my_tuple.append(6). If we run that, we see that tuples don't even have the append method attached.
# tuples do not support this method my_tuple.append(6) # output is the runtime error below # --------------------------------------------------------------------------- # AttributeError Traceback (most recent call last) # <ipython-input-26-d0908ae6cae4> in <module>() # ----> 1 append(my_tuple) # AttributeError: 'tuple' object has no attribute 'append'
But if you want to see a list of all of the methods that are attached to a tuple object, you can type my_tuple, then a period, and then the tab key.
Doing so shows us that tuples only support two methods. Python lists them as count and index. We'll cover these later on. But we’ve seen now that we can’t add new items to a tuple because the append method is not a supported operation for tuples. One aspect of the immutability of tuples is that once defined, they cannot be changed by adding a new item.
Finally, let's verify deletion while using tuples. With lists, we saw that we can easily delete an item using the del built-in function. With our example list, deleting the item at index 0 can be achieved by running del(my_list). If we output the list after using the del function, we see that the integer 10 (which was at index 0) is no longer in the list.
# it's simple to delete an item in a list del(my_list) # output should be [2, 3, 4, 5, 6] print(my_list)
Can we do that with tuples? Let's try it. If I run del(my_tuple), we get another TypeError. This time, the TypeError is informing us that tuples do not support item deletion.
# we cannot delete items from tuples del(my_tuple) # output is the error below # --------------------------------------------------------------------------- # TypeError Traceback (most recent call last) # <ipython-input-29-c044ce86f4d8> in <module>() # ----> 1 del(my_tuple) # TypeError: 'tuple' object doesn't support item deletion
The three constraints we just explored with tuples (no changing an item, no adding an item, and no deleting an item) are dictated by the immutability of tuples. They’re also the difference between tuples and lists. And because tuples are immutable, they also support fewer methods. As we saw, the two methods they do support are count and index. We’ll go over these methods in the next section.
So we’ve determined that we have two tuple methods at our disposal: count and index. Let's open our Jupyter notebook again and try them out.
In Python, the method count will return a count of how many times a given item occurs in a tuple. To explore count, let's set my_tuple to be (1,1,1,2,2,5). If we run my_tuple.count(1), Python returns 3. This makes sense as three integer 1s exist in our tuple. Similarly, if we run my_tuple.count(2), Python returns 2 because there are two integer 2s in the tuple.
# let's use the count method to see how many 1s we have in my_tuple my_tuple = (1,1,1,2,2,5) # output should be 3 my_tuple.count(1) # and how many 2s # output should be 2 my_tuple.count(2)
By the way, when we set the variable my_tuple to (1,1,1,2,2,5) above, you might be tempted to think that we actually changed the tuple, and in turn violated the immutability constraint we discussed in the previous section. That's actually not the case here. Remember, it's not the variable (in our example, my_tuple) that cannot be reassigned. Instead, it's the value itself that is stored in the variable that cannot be reassigned (in our example, (1,1,1,2,2,5)). The value stored in the variable cannot change due to the immutability of tuples.
For example, I am allowed to change my_tuple to a string, like 'hello world'.
# let's change my_tuple to a string my_tuple = 'hello world' # output should be 'hello world' print(my_tuple)
I can do this because Python allows me to reassign a variable to whatever I want. But if I do so, the tuple that I once stored at my_tuple is gone. And when I set my_tuple to (1,1,1,2,2,5) earlier, the old tuple went away and I stored this new tuple into the variable.
This distinction might be confusing at first, but I encourage you to practice thinking of these variable names as just names for objects. These objects can be strings, integer numbers, tuples, or other data types. In Python, a variable name is not the object itself but just the name of the object. When you no longer need that object, you can reuse that name and give it to any other object.
Going back to tuple methods, the other supported method is index. Using index with tuples returns the first index location of a given object in a tuple. Let's change my_tuple back again to my_tuple = (1,1,1,2,2,5). If I run my_tuple.index(1), I'll get 0 because the first 1 in my_tuple is at index 0. And if I run my_tuple.index(2), I get 3 because the first 2 in my_tuple is at index 3.
# let's use the index method to access the items in a tuple # note that the index method will only return the index of the *first* instance of the item my_tuple = (1,1,1,2,2,5) # output should be 0 my_tuple.index(1) # output should be 3 my_tuple.index(2)
You might be wondering at this point why we bother using tuples at all if we can't change them and they're so seemingly inflexible. Believe it or not, the same immutability of tuples that makes them seem inflexible is also sometimes desirable. For example, consider a large program that passes around data. Imagine you want to make sure that no part of the program in any way modifies that data. In this scenario, you’ll use tuples. Lists make it too easy to accidentally change (or mutate) data, so in this application tuples are your best choice. Other programming languages take this data integrity concept so seriously that the majority of the data types they offer (or even all data types they offer) are in fact immutable.
When first starting programming, you're not likely to use tuples very often because you probably won't be working under strict data integrity requirements. However, as you become more advanced, you'll find yourself occasionally needing to mark some data as untouchable. That's when tuples will be your best friend. Keep them in mind for then, but don't feel too stressed if you can't find a reason to use them at first.
That's it for tuples. To summarize, they’re much like lists except that they’re immutable. This means that items inside tuples cannot be changed, new items cannot be added, and existing items cannot be deleted. In the next Intro to Programming article, we'll cover our last data type (and the one with the funniest name): booleans.