In this article, let us learn about the common exceptions in the Dictionaries class in Python, and how to deal with them.
Let’s face it, no programmer is expected to write perfect and flawless Python code that executes without any errors the first time.
Some mistakes are more common than others, as the code does not really behave that we intuitively expect it to. These mistakes are commonly denoted using the term “Pitfalls” and this article is all about looking at the pitfalls when working with Python Dictionaries
Feel free to jump to the section of interest using the table of contents below.
Let us start by refreshing our memories!
Python Dictionaries: A Refresher
A dictionary is a data structure that is used to store Key-Value pairs.
You can create a dictionary
- by using the dict() constructor or
- by using the curly braces {} notation.
Here’s an example that shows both these notations.
# Creating a dictionary using the curly braces notation, each item is a key:value pair
my_dict = {1: "Embedded", 2: "Inventor"}
# Creating a dictionary using the dict() constructor and a list of tuple where each tuple is a (key, value) pair.
my_dict = dict([(1, "Embedded"), (2, "Inventor")])
Both these notations produce the exact same dictionary, the one shown in the figure below.
All keys in a dictionary must be unique and of an immutable type (e.g. strings or numbers), but values can be anything, by anything here I mean any datatype, mutable or immutable. Since you can store any type of value in dictionaries, it is useful in numerous situations as you will experience at various points in your career as a Python programmer!
For example, let’s say you want to store some info about the famous Ringo Starr. You can simply do it like this:
ringo_starr = {"dob": "07 Jul 1940", "band":"The Beatles", "instruments":["drums","vocals"]}
To access a value in a dictionary, you just use the key in square brackets. So if you want to know Ringo’s date of birth, you can do this:
print(ringo_starr["dob"]) # Output: "07 Jul 1940"
You can also nest dictionaries within dictionaries, which lets you store even more information in a hierarchical way. For example:
ringo_starr = {"dob": "07 Jul 1940", "band":"The Beatles", "instruments {"primary":"drums", "secondary":"vocals"}}
I hope now you feel ready to move on to the next part. If anything in this section feels hazy to you I recommend reading our other article which is dedicated to the understanding of Python Dictionaries.
Common errors faced when working with Dictionaries in Python
Pitfall#1: Program crashes with KeyError Exception
This is by far the most common mistake seen with Python beginners.
This pops up when we try to use a key that doesn’t exist in Python. When you see a KeyError, it simply means that the key being looked for could not be found.
Let’s use the hongkong example and call for a key that doesn’t exist:
hongkong= {"Country":"China",
"Population":7500000,
"Attractions":[ "Man Mo Temple", "Victoria Harbor"],
“Country Capital":False
}
hongkong['Official Language']
This results in the following error message:
Traceback (most recent call last):
File "main.py", line 8, in <module>
hongkong['Official Language']
KeyError: 'Official Language'
We can see the bottom line of the error message and make two conclusions:
- The error is called KeyError
- The key that caused it is called ‘Official Language’
Note: When an exception is raised in Python, it is done with a traceback. The traceback gives you all the relevant information to be able to determine why the exception was raised and what caused it.
Learning how to read a Python traceback and understanding what it is telling you is crucial to improving as a Python programmer.
If you wish to learn more about Exceptions we have an excellent article on our website to serve you!
Usually, to access the value of a key, we simply use the following:
hongkong= {"Country":"China",
"Population":7500000,
"Attractions":[ "Man Mo Temple", "Victoria Harbor"],
“Country Capital":False
}
Dict_name[key_name]
If I wanted to get the population of Hong Kong:
hongkong= {"Country":"China",
"Population":7500000,
"Attractions":[ "Man Mo Temple", "Victoria Harbor"],
“Country Capital":False
}
print(hongkong["population"])
OUTPUT
7500000
Another way to get this value is by using the get() method. Have a look at the following code:
hongkong= {"Country":"China",
"Population":7500000,
"Attractions":[ "Man Mo Temple", "Victoria Harbor"],
“Country Capital":False
}
print(hongkong.get("population"))
OUTPUT
7500000
We suggest using the get() method whenever you are unsure whether a key is really present in the dictionary or not. This is because even though both return values as we want, they are internally wired a little bit differently.
Let us see how the get() method behaves differently using an example. Let’s say I want to get the Time Zone of Hong Kong using the get() method.
>>> print(hongkong.get("timezone"))
None
This is as expected since we did not define a key called timezone in our dictionary
However, if I do the same with the first method:
>>> print(hongkong["timezone"])
Traceback (most recent call last):
File "main.py", line 8, in <module>
print(hongkong["timezone"])
KeyError: 'timezone'
We see that we’ve encountered the KeyError exception.
This is the difference between the usual direct access and using the get() method.
The get() method does not return an error when it does not find a value we asked for whereas using the direct method causes the program to pop up an error, this stops the execution of the program altogether, which is simply bad design!
Pitfall#2: Program crashes with ValueError Exception
This arises when we use the update() method incorrectly.
The update() method adds the contents of one dictionary to another. Have a look at the following example:
# vehicles are driven on the left side of the road in Hong Kong
hk_driv_side = {"Road Driving Side": "Left"}
hongkong.update(hk_driv_side)
print(hongkong)
OUTPUT
{'Country': 'China', 'Population': 7500000, 'Attractions': ['Man Mo Temple', 'Victoria Harbor'], 'Country_Capital': False, 'Road Driving Side': 'Left'}
We can see that the original dictionary has been updated! This is how the dict.update() method works.
However as mentioned earlier, when it is used incorrectly. It leads to a ValueError. For instance, take a look at the following example where I try to add the same Road Driving Side key
hongkong= {"Country":"China",
"Population":7500000,
"Attractions":[ "Man Mo Temple", "Victoria Harbor"],
"Country Capital":False}
# vehicles are driven on the left side of the road in Hong Kong
hk_driv_side = {"Left"}
hongkong.update(hk_driv_side)
print(hongkong)
OUTPUT:
Traceback (most recent call last):
File "main.py", line 9, in <module>
hongkong.update(hk_driv_side)
ValueError: dictionary update sequence element #0 has length 4; 2 is required
This is incorrect and resulted in an error because The dict.update() method can be called with a dictionary or an iterable of key/value pairs such as a list of tuples containing 2 elements:
# a list of tuples
hongkong.update([("Road Driving Side","Left")])
Pitfall#3: Copying Dictionaries Incorrectly
If you come from programming languages like C, when you are trying to copy over one variable to another you will intuitively do it the way shown below.
>>> myDict = {1:'a', 2:'b'}
>>> myDictCopy = myDict
>>> myDict
{1: 'a', 2: 'b'}
>>> myDictCopy
{1: 'a', 2: 'b'}
As you can see, both myDict and myDictCopy contain the same values, which is also what we intuitively expect the code to do. But what happens when we try to edit one?
>>> myDictCopy[1] = 'A' # Editing the copy
>>> myDictCopy
{1: 'A', 2: 'b'}
>>> myDict
{1: 'A', 2: 'b'} # Original also changes!
As you can see when we try to edit myDictCopy, myDict also changes! This is definitely not what we wanted the code to do!
So what happened here?
When we wrote the statement
myDictCopy = myDict
what we essentially did was, we created 2 names to refer to the same dictionary.
Just like a person can have an official name and a nickname and both are still referring to the same person, in Python when we use the assignment operator “=” we are essentially creating another name to refer to the same object in memory.
This is illustrated in the figure below.
In works of literature about programming languages, instead of using the word “name“, we use the word “reference” instead as it is referring to an object!
So how to actually create a copy then?
For this Python provides us with the copy() method which is designed to create 2 copies just the way we want. Have a look at the following example
Let us see how to use the copy() method
>>> myDict = {1:'a', 2:'b'}
>>> myDictCopy = myDict.copy()
>>> myDict
{1: 'a', 2: 'b'}
>>> myDictCopy
{1: 'a', 2: 'b'}
As you can see we used the copy() method this time, now let us try editing the copy again!
>>> myDictCopy[1] = 'A'
>>> myDict
{1: 'a', 2: 'b'}
>>> myDictCopy
{1: 'A', 2: 'b'}
Nice! Only the copy changed and not the original!
Another way to create a copy is to use the dict() constructor as shown below.
new_dictionary = dict(old_dictionary)
This creates a completely independent dictionary just as with the copy() method. I let you experiment with this one yourself!
Is this a reference(2nd name) or a copy?
But is there a way to check if a given variable is just another name or is it a true copy?
In fact, there are 2 ways of doing this
Method#1: Using the “is” keyword in Python
The “is” keyword can be used to see if 2 names are referring to the same or different objects as shown below.
>>> myDict = {1:'a', 2:'b'}
>>> myDictCopy = myDict
>>> myDict is myDictCopy
True
>>> myDictCopy = myDict.copy()
>>> myDict is myDictCopy
False
As you can see
- in line-2, we created a reference/2nd-name using the assignment operator.
- in line-3, we used the “is” operator and we got “True” as they are both referring to the same object.
- in line-5, we used the copy() method to make a copy
- in line-6, we used the “is” operator again and this time we got “False” as they are both referring to 2 different objects!
If the above explanation leaves you with more questions than answers and if you wish to learn more about the “is” keyword (and its brother the “is not” keyword), we have an entire article dedicated to it which you can read here!
Method#2: Using the inbuilt id() function
The “is” keyword does the magic that it does using the inbuilt id() function.
Each object created in python has a unique number associated with it just like each automobile has a unique VIN number, as each phone has a unique IMEI number, and as each person has a unique passport number. This can be retrieved using the inbuilt id() function as shown below.
>>> myDict = {1:'a', 2:'b'}
>>> myDictCopy = myDict.copy()
>>> id(myDict)
2480565641216
>>> id(myDictCopy)
2480565709632
As you can see both copies have different id numbers. Now we can make a simple equality comparison to determine if 2 objects are the same or different as shown below.
>>> id(myDict) == id(myDictCopy)
False
If the double-equal-to “==” sign is not something that you have seen before I recommend heading over to the following article to learn more!
The points we have discussed are summarized in the following table.
Pitfall | Reason | Solution |
---|---|---|
KeyError | Trying to access a key that doesn’t exist in the dictionary | Use the get() method to safely access a key and prevent the error |
ValueError | Using the dict.update() method incorrectly | Use the update() method the correct way. |
Copying the proper way | Assignment operator = produces a new reference and not a new object | Use the copy() method or the dict() constructor to create new objects |
Encountering errors is a part and parcel of programming, keep learning and hone your programming skills, which Embedded Inventor will always be helping in!
Here are some other articles that may be of interest to you.
Related articles
Thanks to Namazi Jamal for his contributions to writing this article!