In this article we’ll take a look at KeyErrors in sets and learn why this occurs and how we can avoid them
For those of you in a hurry here is the short version of the answer.
KeyError In Sets And How to Avoid Them: A Straight Answer
KeyErrors in Python are raised when you try to access or invoke an element that does not exist inside a set. For instance:
planets = {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"}
planets.remove("Pluto")
print(planets)
OUTPUT
Traceback (most recent call last):
File "main.py", line 2, in <module>
planets.remove("Pluto")
KeyError: 'Pluto'
To avoid this, python gives us the discard() method as shown below.
planets = {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"}
planets.discard("Pluto")
print(planets)
OUTPUT
{'Mercury', 'Earth', 'Venus', 'Uranus', 'Jupiter', 'Neptune', 'Mars', 'Saturn'}
As you can see, no more errors!
The discard() method is an alternative for the remove() method if you want to silently ignore if the element we wish to remove does not exist!
embeddedinventor.com
Wait, there is more!
The rest of the article focuses on answering the following questions
- What does Keys have to do with sets?
- When to use remove() and when to use discard()?
- How to avoid KeyError while using remove()?
- How discard() method is implemented internally?
Intrigued? Alright then, lets go ahead and explore the answers to these questions!
KeyError In Sets And How to Avoid Them: A Detailed Answer
I remember the first time when I came across this particular exception, it was when I was working with python dictionaries. The name of the exception was straight forward as dictionaries have keys and KeyError basically meant that the key I was looking for did not exist in the dictionary. (If you wish to learn more here is an article about KeyError in dictionaries)
I always thought “keys” were just a “Dictionary” thing, then why did we just get KeyError while working with sets?
What does keys have to do with sets?
As we know, Sets are a data structure in Python that we use when we need a collection of unique items. (Click here to learn more about Python Sets)
So to answer the question:
Keys are unique in dictionaries, set only store unique elements, hence internally the elements of a set are implemented the same way as keys in dictionaries!
embeddedinventor.com
In other words
Members of a sets are implemented as Keys in Python.
embeddedinventor.com
So the next time you get KeyError while working with sets, remind yourself this! Keys are the same as elements here!
What causes KeyError?
While sets are iterable, they are unordered and unindexed. This means that we cannot access or invoke a set element in the usual way we do for lists.
We can use a for loop to print every element in the set. For example:
basket = {'ice cream', 'salad', 'custard'}
for x in basket:
print(x)
OUTPUT
salad
custard
ice cream
Now if we tried to remove something that didn’t exist in a set, this is where we’ll be hit with a KeyError. Let’s try to delete the item ‘cake’, an element that I never added to my set in the first place:
basket = {'ice cream', 'salad', 'custard'}
basket.remove('cake')
print(basket)
OUTPUT
File "main.py", line 2, in <module>
basket.remove('cake')
KeyError: 'cake'
How to avoid KeyError
As we saw in the “Straight Answer” section, we can just substitute the remove() method with the discard() method.
basket = {'ice cream', 'salad', 'custard'}
basket.discard('cake')
print(basket)
OUTPUT
{'custard', 'ice cream', 'salad'}
It is nice that the solution is so simple, but then
- why do we even have the remove() method if discard() is so awesome?
- how does this work? and more importantly
- is this the right solution for all scenarios?
Lets go ahead and see!
remove() vs discard(): When to use which one?
Lets take a quick look at the documentation for each of these methods.
This is what python tells us about the remove() method
And this is what python tells us about the discard() method
If you compare the last lines from the screenshots above, you can clearly see that
The remove() method raises KeyError if element is not a member, while the discard() method does nothing!
The main point to note here is that the discard() method does nothing!
Which means if we wish to do something about the fact the member is not present, then we do not have the option while using the discard() method!
That is where the remove() method comes into the picture!
But then it also crashes our program when an element is not member!
Lets see how to avoid this!
2 ways to avoid KeyError while using remove()
There are basically 2 options you can choose from when it comes to avoiding KeyError while working with Python Sets, lets have a look at them one by one and see
- how to implement each option &
- when to choose which option
Option#1: Using an if statement
You can use an if statement to verify an element exists in a set before using it in a statement. For instance, let me do the same example we did previously when trying to remove a non-existing element, but with a conditional statement:
basket = {'ice cream', 'salad', 'custard'}
if 'cake' in basket:
basket.remove('cake')
print("Removed cake from basket!")
else:
print("No cake in basket!")
OUTPUT
No cake in basket!
The if condition ensures a fail proof method by allowing the error-causing statement to be executed only after the membership of the element in the set is verified.
And this is option#1 to avoid a KeyError when dealing with sets!
If you wish to learn more about the membership operator “in” and its brother “not in” give the below article a quick read!
When to use option#1?
This option is useful if your code is relatively simple and all you want to do is handle the KeyError exception raised by the remove() method.
In case of more complicated code (which is usually the case in industry!), in addition to KeyError our code might produce multiple other exceptions. In such situations, the better way to do it is using a try-except block.
Option#2: Using try-except block
The try-block feature is an extremely handy tool that python provides. It conveniently catches and deals with any code that might cause an error in your program script. Read to know how you can catch exceptions using try-block.
If the try-except block feels a bit confusing, we have a whole other article dedicated to explaining the concept in detail, I suggest reading that one in the link below before continuing this one!
Exceptions in Python: Everything You Need To Know!
Let’s try the program we previously did one more time, removing a non existing element to a set using try-block:
basket = {'ice cream', 'salad', 'custard'}
try:
basket.remove('cake')
except KeyError:
print("This element does not exist in the set")
OUTPUT
This element does not exist in the set
When to use option#2?
As mentioned previously, if, in addition to KeyError, your code might also produce multiple other exceptions option#2 is the go-to one!
For example, say you have a text file with a few lines of text and you wish to process them as follows
- filter out the duplicate lines, this can be done by reading each line and storing them in a set.
- See if a “cake” is one of the lines, then remove that element from the set
In this case our code can produce 2 exceptions: FileNotFoundError and KeyError, you can handle them as shown below.
try:
my_file = open("basket.txt", 'r')
my_lines = my_file.readlines()
my_unique_lines = set()
for line in my_lines:
my_unique_lines.add(line)
my_unique_lines.remove("cake\n")
print("Cake deleted")
except FileNotFoundError:
print("Cannot find the file mentioned")
except KeyError:
print("No cake in basket")
I hope this complex example did not get into the way of the teaching point!
To put it in simpler terms
If there is already a need for try-except block, then add the KeyError as one of the except statements!
embeddedinventor.com
If you wish to learn more about handling complex try-except blocks check out the article linked below!
Python catch multiple exceptions
Lets have a quick look at the implementation logic of the discard() method before ending this article!
How is discard() implemented?
I have not personally looked at the source code myself, but my guess is, it uses the logic in option#1 without the else statement as shown below!
basket = {'ice cream', 'salad', 'custard'}
if 'cake' in basket:
basket.remove('cake')
If you are interested I leave it to you to hunt down the source code of python and see how it is implemented in the background! If you managed to solve it, be sure to send me the answer!
Here is another pro tip for you, to fix not just KeyError but any error in Python. Whenever your program crashes with an Exception, (take a deep breath and) have a look at the huge wall of error text that just got printed on your screen!
There is actually a lot of information contained in the error message, if you wish to learn the tricks then set aside 10mins of your time and read the following article!
Python: Details contained in an Exception
For visual learners out there we have also made an interesting video on that topic!
And with that I will end this article.
Congratulations for making it till the end of the article, not many have the perseverance to do so!
I hope you enjoyed reading this article and found it useful!
Feel free to share it with your friends and colleagues!
If your thirst for knowledge have not been quenched yet, here are some related articles that might spark your interest!
Related Articles
Lambda Functions in Python: Explained Using 4 Examples!
Mutable and Immutable Data Types in python explain using examples
Python Sets: Everything you need to know!
Python Dicts: Most Common Exceptions and How to Avoid Them!
Thanks to Namazi Jamal for his contributions in writing this article!