Months of testing and bug fixes can save you hours of planning.
Hey pips!
Last time we looked at sets in Python. Today, we’ll be exploring another far more commonly used iterable. There’s a strange little collection type in Python and many other languages known as a tuple. At first glance, it appears to be almost identical to a list
:
>>> a_list = [7, 8, 9]
>>> a_tuple = (7, 8, 9)
We’re just using a different mind of bracket. You can even omit the ()
entirely, though it’s probably best to leave them in for clarity:
>>> a_tuple_too = 2, 0, 0
>>> print(a_tuple_too)
(2, 0, 0)
Indexing is the same:
>>> a_list[0]
7
>>> a_tuple[0]
7
>>> a_list[-1]
9
>>> a_tuple[-1]
9
And we can iterate over them:
>>> [-each for each in [-1, 0, 1]]
[1, 0, -1]
>>> [-each for each in (-1, 0, 1)]
[-1, 0, 1]
Note
There aren’t ‘set comprehensions’ though. Wrapping an x for x in y
expression in ()
produces a generator, which we’ll explore in a later issue!
In fact, even if you’ve never used tuples, there’s a good chance you’ll have encountered this bug before:
>>> questionable_variable = some_complicated_expression(),
Say we’re expecting a str
to be returned from our expression. What’s the type of questionable_variable
then?
>>> type(questionable_variable)
<class 'tuple'>
Wait, why’s it a tuple?
Notice the sneaky ,
hiding at the end of the line! Since the ()
aren’t mandatory, that comma actually turns the expression into a tuple. To illustrate this more simply:
>>> x = (2)
>>> type(x)
<class 'int'>
>>> y = (2,)
>>> type(y)
<class 'tuple'>
Looks a bit weird, doesn’t it? Luckily, we rarely ever use single-item tuples.
So with all that, why do tuples even exist?
The singular and critical difference between tuples and lists is that tuples are immutable. This means you can’t modify them – once a tuple is created, no items can be added or removed.
>>> name = ("Josh", "Millman")
>>> name[0] = "j"
TypeError: 'tuple' object does not support item assignment
Because they’re immutable, tuples are also hashable, meaning a unique hash key can be generated for them (this isn’t the case for the mutable list
). We’ll see why this is useful when we explore dictionaries in the future.
Tuples are really nice for when you want a lightweight object, like when returning a small collection of values from a function. There’s a lot less overhead when instantiating and handling tuples than lists, since Python doesn’t need to worry about the tuple being modified.
def translate_point(point: tuple, vector: tuple) -> tuple:
return (point[0] + vector[0], point[1] + vector[1])
While we’re at it, we can also make this a little more readable by making it more obvious it’s a tuple:
def translate_point(point: tuple, vector: tuple) -> tuple:
return (
point[0] + vector[0],
point[1] + vector[1]
)
Let’s see now what this function will output:
>>> p = (4, 2)
>>> t = (1, 10)
>>> translate_point(p, t)
(5, 12)
>>> translate_point(translate_point(p, t))
(6, 22)
We tend to omit brackets when grouping variables, usually after return
, yield
, or some other statement.
def end_issue() -> tuple:
index = "06"
message = "we’ll be right back!"
return index, message
Can you write a one-liner to reverse a tuple of any length? (Since tuples are immutable, you’ll need to create a new tuple ;)
>>> t = (4, 3, 9, 9)
>>> (your_expression)
(9, 9, 3, 4)