Finding Your Way To The Right Value • Python's Mappings (Data Structure Categories #3)
Mappings • Part 3 of the Data Structure Categories Series
If you need to find your way around town, you'll need a map. And a Python mapping enables you to find your way from one object to another.
As we saw in the previous article in this series, the order of the items in a sequence matters. In a mapping, the relationship between a pair of objects is what matters. We refer to the pair of objects as the key and the value.
Let's see what makes a mapping and dig a bit beneath the surface to understand this category of data structures.
The Data Structure Categories Series
We're on the third of seven articles in this series. You can read the previous ones by following the links in this overview:
What's a Mapping? The Short Version
Rather than referring to items based on their position, as you do in a sequence, in a mapping you refer to them based on a label you assign to each item. This "label" is another Python object. The "label" is called the key, and the object it's associated with is the value. Any Python object can be used as a value in a dictionary. However, only objects with a unique and stable identifier throughout their lifetime—we call these hashable objects—can be used as keys.
Let's look at an example using a dictionary, which is the most common mapping in Python:
But you know this already. So let's dive into mappings further.
What's a Mapping? The More Detailed Version
We can start by looking at what we can do with mappings. Can we determine the length of a mapping?
Yes, we can. Therefore, a mapping is sized. This means the class has __len__()
defined.
Is a mapping iterable? In other words, can we loop through it using a for
loop?
Yes, we can loop through a dictionary. The loop iterates through the keys of the dictionary. All mappings are iterable, which means they have at least one of __iter__()
or __getitem__()
defined, as we've seen in the first article in the series.
You can also fetch an item from a mapping using the square brackets. This means that __getitem__()
is defined in the class.
There's one other special method defined in mappings: __contains__()
. This dunder method makes an object a container. We'll talk about containers later in this series.
Let's confirm that a dictionary has all these special methods:
Sequences also have these special methods defined. Well, technically __iter__()
is not required in either sequences or mappings as __getitem__()
is sufficient to make an object iterable. However, most sequences and mappings will have __iter__()
defined, as this is the preferred protocol for iteration.
The main difference between sequences and mappings is in the behaviour of the __getitem__()
special method. For sequences, __getitem__()
only accepts integers or slices. However, __getitem__()
can handle other data types for mappings, and it deals with fetching the value mapped to the key.
A Mapping's Ethos
Let's get back to the main feature that defines a mapping. What matters in a mapping is the relationship between the key and its value. This is a significant difference from sequences. In a sequence, what matters is the position of an object within the sequence.
If you need to fetch an item in a sequence, you use its position, such as my_sequence[2]
. If you need to fetch an item in a mapping, you use the key it's associated with, such as my_mapping[key]
.
Let's look again at Python's king of the mappings, the dictionary. But let's first travel back in time to the era before Python 3.6. The order in which items were added to a dictionary was meaningless before Python 3.6. After all, what matters is the association between the key and its value.
In Python 3.6, the internal workings of dictionaries changed within CPython, the main Python implementation. The order of items is now maintained in dictionaries because of this change. This was not the update's main purpose, so ordered dictionaries were a by-product of this change. However, this ordering of dictionaries was formalised in Python 3.7. Therefore, dictionaries are guaranteed to be ordered in all Python versions from 3.7 onwards.
But this doesn't change the nature of dictionaries. They're still mappings, and the key-value association is what matters. The ordering is a bonus.
Let's confirm that order doesn't matter in a dictionary. But let's start with a couple of sequences first. These two lists have the same values but in a different order:
The lists are not equal since the order of the items is different.
And now, back to mappings. Let's repeat the exercise with two dictionaries with the same items but in a different order:
The two dictionaries are equal. Even though the order of the items is different, the key-value pairs are all the same, which means the dictionaries are equal to each other.
We've covered three data structure categories in this series so far. Sequences and mappings, the second and third in the series, have a lot in common. One of their similarities is that they're both iterables, which is the first category we looked at. You can see a hierarchy starting to form, and we'll map out (pun unintended) this hierarchy over the rest of this series.
Next in the series are containers and collections, and we'll talk about sized objects along the way. That will give us a good overview of the main categories of data structures and how they're connected.
We'll finish the series with iterators and generators, but let's not run ahead.
Etymology Corner
The origins of the word "mapping" are not as helpful as the etymologies of "iterable" and "sequence".
A map, for example, shows us where countries are in the world by mapping their names to a geographical location. A Python mapping performs a similar task with the keywords and the objects they “map” onto.
But etymology is fun anyway, so let's look at where "mapping" comes from. The Latin mappa means "a cloth". And many years ago, the original maps were drawn on cloth.
Code in this article uses Python 3.11
Stop Stack
Recently published articles on The Stack:
Python's functools.partial() Lets You Pre-Fill A Function. An exploration of partial() and partialmethod() in functools
Let The Real Magic Begin • Creating Classes in Python. Year 2 at Hogwarts School of Codecraft and Algorithmancy • Defining Classes • Data Attributes
Sequences in Python. Sequences are different from iterables • Part 2 of the Data Structure Categories Series
Harry Potter and The Object-Oriented Programming Paradigm. Year 1 at Hogwarts School of Codecraft and Algorithmancy • The Mindset
Iterable: Python's Stepping Stones. What makes an iterable iterable? Part 1 of the Data Structure Categories Series
The next cohort of The Python Coding Programme starts next week. Live sessions with very small cohorts over 3 weeks, with 90 minutes live on Zoom every day (4 days a week). Each cohort only has 4 participants and there's active mentoring throughout with a private forum for the cohort to continue discussions. Here's some more information about The Python Coding Programme for Beginners.
Most articles will be published in full on the free subscription. However, a lot of effort and time goes into crafting and preparing these articles. If you enjoy the content and find it useful, and if you're in a position to do so, you can become a paid subscriber. In addition to supporting this work, you'll get access to the full archive of articles and some paid-only articles. Thank you!