Python : Dictionaries


Python dictionaries are something completely different —they are not sequences at all, but are instead known as mappings. Mappings are also collections of other objects, but they store objects by key instead of by relative position. In fact, mappings don’t maintain any reliable left-to-right order; they simply map keys to associated values. Dictionaries, the only mapping type in Python’s core objects set, are also mutable: they may be changed in-place and can grow and shrink on demand, like lists.

Mapping Operations

When written as literals, dictionaries are coded in curly braces and consist of a series
of “key: value” pairs. Dictionaries are useful anytime we need to associate a set of values with keys—to describe the properties of something, for instance. As an example, consider the following three-item dictionary (with keys “food,” “quantity,” and “color”):

>>> D = {'food':'biryani', 'quantity':3, 'color':'yellow'}

We can index this dictionary by key to fetch and change the keys’ associated values. The dictionary index operation uses the same syntax as that used for sequences, but the item in the square brackets is a key, not a relative position:

>>> D['food']           # Fetch the value of key 'food'
>>> D['quantity'] += 1  # Add one to 'quantity' value
>>> D
{'food': 'biryani', 'color': 'yellow', 'quantity': 4}

Although the curly-braces literal form does see use, it is perhaps more common to see dictionaries built up in different ways. The following code, for example, starts with an empty dictionary and fills it out one key at a time. Unlike out-of-bounds assignments in lists, which are forbidden, assignments to new dictionary keys create those keys:

>>> D={}
>>> D['name'] = 'deepak'   # Create keys by assignment
>>> D['age'] = 25
>>> D['job'] = 'dev'

>>> D
{'job': 'dev', 'age': 25, 'name': 'deepak'}

>>> D['name']

Here, we’re effectively using dictionary keys as field names in a record that describes someone. In other applications, dictionaries can also be used to replace searching operations—indexing a dictionary by key is often the fastest way to code a search in Python.

In the prior example, we used a dictionary to describe a person (well…me!), with three keys. Suppose, though, that the information is more complex. Perhaps we need to record a first name and a last name, along with multiple job titles. This leads to another application of Python’s object nesting in action. The following dictionary, coded all at once as a literal, captures more structured information:

>>> rec = { 'name': {'first':'Deepak','last':'Devanand'},
	    'job': ['dev','ceo'],

Here, we again have a three-key dictionary at the top (keys “name,” “job,” and “age”), but the values have become more complex: a nested dictionary for the name to support multiple parts, and a nested list for the job to support multiple roles and future expansion. We can access the components of this structure much as we did for our matrix earlier, but this time some of our indexes are dictionary keys, not list offsets:

>>> rec['name']   # 'name' is a nested dictionary
{'last': 'Devanand', 'first': 'Deepak'}

>>> rec['name']['last']  # Index the nested dictionary

>>> rec['job']   # 'job' is a nested list
['dev', 'ceo']

>>> rec['job'][-1]  # Index the nested list

>>> rec['job'].append('guitarist') # expand Deepak's job list in-place
>>> rec
{'age': 25, 'job': ['dev', 'ceo', 'guitarist'], 'name': {'last': 'Devanand', 'first': 'Deepak'}}

Notice how the last operation here expands the nested job list—because the job list is a separate piece of memory from the dictionary that contains it, it can grow and shrink freely.

As you can see, nesting allows us to build up complex information structures directly and easily. Building a similar structure in a low-level language like C would be tedious and require much more code: we would have to lay out and declare structures and arrays, fill out values, link everything together, and so on. In Python, this is all automatic—running the expression creates the entire nested object structure for us. In fact, this is one of the main benefits of scripting languages like Python.

Just as importantly, in a lower-level language we would have to be careful to clean up all of the object’s space when we no longer need it. In Python, when we lose the last reference to the object—by assigning its variable to something else, for example—all of the memory space occupied by that object’s structure is automatically cleaned up for us:

>>> rec = 0    # Now the object's memory space is reclaimed

Technically speaking, Python has a feature known as garbage collection that cleans up unused memory as your program runs and frees you from having to manage such details in your code. In Python, the space is reclaimed immediately, as soon as the last reference to an object is removed.

Sorting Keys : for Loops
Though dictionaries only support accessing items by key, they also support type-specific operations with method calls that are useful in a variety of common use cases.

As mentioned earlier, because dictionaries are not sequences, they don’t maintain any dependable left-to-right order. This means that if we make a dictionary and print it back, its keys may come back in a different order than that in which we typed them:

>>> D = {'a':1, 'b':2, 'c':3}
>>> D
{'a': 1, 'c': 3, 'b': 2}

What do we do, though, if we do need to impose an ordering on a dictionary’s items?
One common solution is to grab a list of keys with the dictionary keys method, sort that with the list sort method, and then step through the result with a Python for loop.

>>> Ks = list(D.keys())  # Unordered keys list
>>> Ks
['a', 'c', 'b']

>>> Ks.sort()  # Sorted keys list
>>> Ks
['a', 'b', 'c']

>>> for key in Ks:   # Iterate through sorted keys
	print(key, '=>', D[key])

('a', '=>', 1)
('b', '=>', 2)
('c', '=>', 3)

This is a three-step process, although, in recent versions of Python it can be done in one step with the newer sorted built-in function. The sorted call returns the result and sorts a variety of object types, in this case sorting dictionary keys automatically:

>>> D
{'a': 1, 'c': 3, 'b': 2}

>>> for key in sorted(D):

('a', '=>;', 1)
('b', '=>;', 2)
('c', '=>;', 3)

Besides showcasing dictionaries, this use case serves to introduce the Python for loop. The for loop is a simple and efficient way to step through all the items in a sequence and run a block of code for each item in turn. A user-defined loop variable (key, here) is used to reference the current item each time through. The net effect in our example is to print the unordered dictionary’s keys and values, in sorted-key order.

The for loop, and its more general cousin the while loop, are the main ways we code
repetitive tasks as statements in our scripts. Really, though, the for loop (like its relative the list comprehension, which we met earlier) is a sequence operation. It works on any object that is a sequence and, like the list comprehension, even on some things that are not. Here, for example, it is stepping across the characters in a string, printing the uppercase version of each as it goes:

>>> for c in 'magic':
	print c.upper()


Python’s while loop is a more general sort of looping tool, not limited to stepping across sequences:

>>> x = 4
>>> while x > 0:
	x -= 1


Missing Keys : if Tests
Although we can assign to a new key to expand a dictionary, fetching a nonexistent key is still a mistake:

>>> D
{'a': 1, 'c': 3, 'b': 2}

>>> D['a']      # Assigning a new key grows the dictionary

>>> D[10]      # Referencing a non-existent key is an error
... error text ...
KeyError: 10

This is what we want—it’s usually a programming error to fetch something that isn’t
really there. But in some generic programs, we can’t always know what keys will be
present when we write our code. How do we handle such cases and avoid errors? One
trick is to test ahead of time. The dictionary in membership expression allows us to query the existence of a key and branch on the result with a Python if statement.

>>> 'a' in D

>>> if not 'a' in D:
	print 'missing'

Alternatively, one can use the has_key method:

>>> D.has_key('a')
>>> D.has_key('x')

The get method takes the key and returns its value if the key exists, otherwise returns None.

>>> D.get('b')
>>> D.get('x')

About Deepak Devanand

Seeker of knowledge
This entry was posted in Python and tagged , , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s