One of a kind…

The Python Dictionary

Antoine Ghilissen
6 min readSep 16, 2020

--

A dictionary is a built-in data type. It is a mapping object (a mapping object maps hashable values to arbitrary objects). Actually, it is the only mapping object in Python! (Dictionaries are called “associative arrays/memories” in other programming languages.)

It is often compared to lists for various reasons (both of them are collections of objects).

Dictionaries share the following common characteristics with lists. Both:

  • are mutable. Once created, values can be added/updated/deleted.
  • are dynamic. The length/size of the dictionary will vary depending on its content.
  • can be nested. Dictionary of lists or dictionary of dictionaries is a thing.

They differ from lists/sequences:

  • dictionaries are unordered. Dictionary values are accessed by using keys, unlike lists where the position of the item is used (those keys are immutable as well).

In this article, we are going to cover the built-in methods for dictionaries, have a quick look at what seems to be a dictionary: the zip() method and, finally, we are going to understand how dictionary comprehension works.

Built-ins

Creating a dictionary

There are many ways to create dictionaries. The most convenient methods are the {}method and by using the dict() method ({} is faster than dict()):

>>> %%time
>>> dictionary = {'apricots': 4.5,
>>> 'blueberries': 12,
>>> 'cucumbers': 1.2,
>>> 'dates': 6.5
>>> }
>>> print(dictionary)
CPU times: user 4 µs, sys: 1 µs, total: 5 µs
Wall time: 7.87 µs
{'apricots': 4.5, 'blueberries': 12, 'cucumbers': 1.2, 'dates': 6.5}
>>> %%time
>>> dictionary = dict([('apricots', 4.5),
>>> ('blueberries', 12),
>>> ('cucumbers', 1.2),
>>> ('dates', 6.5)]
>>> )
>>> print(dictionary)
CPU times: user 7 µs, sys: 1 µs, total: 8 µs
Wall time: 10.5 µs
{'apricots': 4.5, 'blueberries': 12, 'cucumbers': 1.2, 'dates': 6.5}

These methods are the most common methods to create dictionaries from scratch. There are many other ways to stitch up data into a dictionary depending on which form the data is available: lists, variables, iterators, or, more simply, when user input is required (for example).

As said before, dictionary keys are immutable:

Reminder:

int, float, complex, string, tuple, frozen set and bytes are immutables.

list, dict, set and byte array are mutable.

>>> d = {(1,2): 'tuples',
>>> (1,3): 'can',
>>> (1,4): 'be',
>>> (1,5): 'keys'
>>> }
>>> print(d)

{(1, 2): 'tuples', (1, 3): 'can', (1, 4): 'be', (1, 5): 'keys'}
>>> d = {['lists', "can't", 'be', 'dictionary', 'keys']: 'Oopsie!'}

--------------------------------------------------------------------
TypeError Traceback (most recent call last)

<ipython-input-3-781cb5459fc7> in <module>
6 print(d)
7
----> 8 d = {['lists', "can't", 'be', 'dictionary', 'keys']: 'Oopsie!'}

TypeError: unhashable type: 'list'

Access dictionary value

>>> dictionary['apricots']4.5

Because dictionaries (and lists) are mutable, one can:

Add value to dictionary (or update)

# Update values
>>> dictionary['apricots'] = 4.7
# Add values
>>> dictionary['elderberries'] = 14
# or
>>> dictionary.update([('feijoa', 6)])
{'apricots': 4.7,
'blueberries': 12,
'cucumbers': 1.2,
'dates': 6.5,
'elderberries': 14,
'feijoa': 6}

If a value doesn’t exist, a KeyError will be raised. One way to get around it is to use the .get() method.

>>> dictionary['Granadilla']--------------------------------------------------------------------
KeyError Traceback (most recent call last)

<ipython-input-31-87ef655b7951> in <module>
----> 1 dictionary['Granadilla']

KeyError: 'Granadilla'

If I wanted to make a word counter for example (typically, when the script reads a new word, it is not in the dictionary yet and a KeyError is raised):

>>> document = "The quick brown fox jumps over the lazy dog">>> counter = {}
>>> for word in document.lower().split():
>>> counter[word] = counter.get(word, 0) + 1
{'the': 2,
'quick': 1,
'brown': 1,
'fox': 1,
'jumps': 1,
'over': 1,
'lazy': 1,
'dog': 1}

line 1: The dictionary counter is created, empty.

line 2: The for loop is going through the document; .lower() is used to convert every character to lower case whilst .split() breaks the document down into words (every space character is a break).

line 3: In the for loop, we are looking for the value “word”, if it doesn’t exist it is then created and set to 0. Once it exists, 1 is added to it (if it already existed before, we still add 1 to the previous value).

Remove a value from a dictionary

Either delete the pair key:value:

>>> del dictionary['elderberries']{'apricots': 4.7,
'blueberries': 12,
'cucumbers': 1.2,
'dates': 6.5,
'feijoa': 6}

Either pop the value:

>>> dictionary.pop('cucumbers'){'apricots': 4.7, 'blueberries': 12, 'dates': 6.5, 'feijoa': 6}

One can remove the last added pair using dictionary.popitem() (Last In - First Out principle). Or ALL the values from a dictionary can be reoved at once:

>>> dictionary.clear(){}>>> dictionary = {'apricots': 4.5, 
'blueberries': 12,
'cucumbers': 1.2,
'dates': 6.5
}

Length of a dictionary

len will conveniently count the number of pairs within a given dictionary

>>> len(dictionary)4

Membership and dictionary

The membership tests the keys of a dictionary, not their values:

>>> 'dates' in dictionaryTrue>>> 'apples' not in dictionaryTrue>>> 12 in dictionary
# (12 is the price of blueberries)
False

View objects

These objects provide a dynamic view of the dictionary. They can be iterated upon and support membership too:

>>> dictionary.items()dict_items([('apricots', 4.5), ('blueberries', 12), ('cucumbers', 1.2), ('dates', 6.5)])>>> dictionary.keys()dict_keys(['apricots', 'blueberries', 'cucumbers', 'dates'])>>> dictionary.values()dict_values([4.5, 12, 1.2, 6.5])Iteration on view object
>>> for key, value in dictionary.items():
>>> print(key+ ':', value)
apricots: 4.5
blueberries: 12
cucumbers: 1.2
dates: 6.5

Iterating on the dictionary object directly doesn’t work as on would expect. (note that the keys can be iterated upon, but it won’t reveal the associated values: for keys in dictionary: print(key))

>>> for key, value in dictionary:
>>> print(key+ ':', value)
--------------------------------------------------------------------
ValueError Traceback (most recent call last)

<ipython-input-34-42e38a312a9d> in <module>
----> 1 for key, value in dictionary:
2 print(key+ ':', value)

ValueError: too many values to unpack (expected 2)

Zip it… not!

A(terrible) way to create a dictionary is by zipping 2 lists together. This will result in a zip object, not a dictionary. A zip object is an iterator and only yields values when asked for it so values can’t be randomly checked, the object has an unknown length, etc.

>>> zipp = zip(['apricots', 'blueberries', 'cucumbers', 'dates'],
[4.5, 12, 1.2, 6.5]
)
>>> print(zipp)
<zip object at 0x7fac20077040>>>> print(zipp['apricots'])--------------------------------------------------------------------
TypeError Traceback (most recent call last)

<ipython-input-21-e32100618c12> in <module>
3 )
4 print(zipp)
----> 5 print(zipp['apricots'])

TypeError: 'zip' object is not subscriptable

Ziping will come in handy in a few situations, though:

  • if there is the need for “triple” (or more) tuples.
  • in a for loop. For a 'dictionary' behaviour (or at least its key:value property).
>>> list(zip((1, 2, 3, 4),('a', 'b', 'c', 'd'), (True, False, True, False)))[(1, 'a', True), (2, 'b', False), (3, 'c', True), (4, 'd', False)]>>> for k, v in zip(['apricots', 'blueberries', 'cucumbers', 'dates'], [4.5, 12, 1.2, 6.5]):
>>> print(k +':', v)
apricots: 4.5
blueberries: 12
cucumbers: 1.2
dates: 6.5

Dictionary comprehension

I t is a very powerful (and faster) way to create a dictionary. Dictionary comprehension can replace a lambda function, have conditional statements, be nested, etc. It works just like lists comprehensions!

>>> %%time
>>> powers = {n: n**2 for n in range(10)}
CPU times: user 20 µs, sys: 0 ns, total: 20 µs
Wall time: 21.9 µs

{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

Instead of:

>>> %%time
>>> powers = {}
>>> for n in range(10):
>>> powers[n] = n **2
CPU times: user 107 µs, sys: 0 ns, total: 107 µs
Wall time: 121 µs

{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

With a condition, dictionary comprehensions looks similar to this:

>>> odd_powers = {n: n**2 for n in range(10) if n%2==0}{0: 0, 2: 4, 4: 16, 6: 36, 8: 64}

Replacing lambda

Without using a comprehension, the easiest way to convert some temperature readings is by using a lambda/map approach:

>>> temperatures_f = {'t1': -40, 't2': -20, 't3': -10, 't4': 0, 't5': 10, 't6': 100}>>> %%time
>>> celsius = list(map(lambda x:(x-32)/1.8, temperatures_f.values()))
>>> temperatures_c = dict(zip(temperatures_f.keys(), celsius))
CPU times: user 25 µs, sys: 0 ns, total: 25 µs
Wall time: 28.1 µs

{'t1': -40.0,
't2': -28.88888888888889,
't3': -23.333333333333332,
't4': -17.77777777777778,
't5': -12.222222222222221,
't6': 37.77777777777778}

With the dictionary comprehension, it look like the following:

>>> %%time
>>> temperatures_c = {k:(v-32)/1.8 for (k,v) in temperatures_f.items()}
CPU times: user 15 µs, sys: 2 µs, total: 17 µs
Wall time: 20.3 µs

{'t1': -40.0,
't2': -28.88888888888889,
't3': -23.333333333333332,
't4': -17.77777777777778,
't5': -12.222222222222221,
't6': 37.77777777777778}

Conclusion

I n this article, we have covered most of the built-in methods and the fundamental concepts associated with dictionaries (complete list of built-in methods here).

They are one of the most used data types in Python as they are a very convenient way to store data (the key:value pair is how most data is stored now-a-day). I hope this article has made you more comfortable with dictionaries and you already are planning to refactor your code using dictionary comprehension! 😄

--

--