loading...
Cover image for Why you should never use the dict.get() method in Python

Why you should never use the dict.get() method in Python

prahladyeri profile image Prahlad Yeri Originally published at prahladyeri.com ・2 min read

(Originally published on prahladyeri.com)

There is an increasing trend towards using the dict.get() method in Python, its very widely used in many tutorials and examples too. Condsider the below example for instance:

>>> dd = {'a': 1}
>>> dd.get('a')
1

This simple dictionary dd has only one key ('a') and its corresponding value is 1. The proper way of accessing this key is to refer it as dd['a'] but you need to be sure that the dictionary has that key otherwise it'll throw an error! Hence, as a shortcut way, programmers have started using dd.get() method instead. This method simply returns the key value if its present and None if there is no such key in the dictionary.

Seems good enough, right? Well, don't fall into that trap so soon! In the real world, you'll come across a situation sooner or later when you'll have to handle a dictionary key who's value is None! What will you do then? Consider this example:

>>> dd = {'a': None}
>>> dd.get('a')

In this case, you'll never know whether a key was present in the dictionary or one was there and its value was None. Knowing the difference could be crucial in some situations and not doing so might cause subtle bugs in your program which will be very hard to trace later. The best habit to access a dictionary is this:

>>> if 'a' in dd:
>>>     x = dd['a'] # do whatever
>>> else:
>>>     pass # handle this

Or you can even simply refer its key directly if you are sure that the key exists:

>>> x = dd['a'] # do whatever

Similarly, there are two different ways to remove an item from dictionary but there is nothing wrong in using either of them (except of course that you need to be sure that the key is present and use an exception handler if you aren't):

>>> dd.pop('a')
>>> del dd['a']

Discussion

pic
Editor guide
Collapse
hanpari profile image
Pavel Morava

Oh, not so fast. I have several reasons to disagree.
First of all, you omitted to write full signature of the method.


dict.get(self, key, default=None)

You see, there is a default parameter which doesn't need to be None. It can be whatever you find relevant, even KeyError if you insist.

{"a":1}.get("b", KeyError("This key is not in this dictionairy."))
KeyError('This key is not in this dictionairy.')

But not only that, you should understand None is not like null in Java. None is a different type in Python.
What do I mean? Let's use typing module to make everything clear.

from typing import Dict, Optional

users: Dict[str, str]
users = {
    "user1": "address1",
    "user2": "address2"
}

user: Optional[str] = users.get("user3")

Clearly, there should be no confusion between type str and None. None indicates value which is missing. I think it is perfectly fine to use get method. In the case, you need to distinguish between not-existent key and None value in dictionairy, you have free options.

users.get("user?", "What user?")
'What user?'
users["user?"] if "user?" in users else "What user?"
'What user?'
try:
    users["user?"]
except KeyError:
    "What user?"

I saw try-except variant being recommended in concurrent environment as thread-safe. But honestly, I see no problem with get method whatsoever.

Collapse
kfirstri profile image
Kfir Stri

Cool post Prahlad! I think 'never' is a bit harsh, I agree that checking if the key exists before or if you know the key will be in the dict you should access it directly.. but, the get method is useful sometimes for default values, not just to return None..

If you got some dict full with booleans for some reason, instead of writing -

if 'some_bool' in bool_dict:
   result = bool_dict['some_bool']
else:
   result = False

It is nicer to write

result = bool_dict.get('some_bool', False)

Or many other use cases where you can use the default argument to make the .get method return something other than None (maybe your program has some default config parameters, or maybe you want to set a different string.. and so on)

Collapse
rhymes profile image
rhymes

Letting Python raise an exception and catch it is also valid :)

try:
  x = d['a']
except KeyError:
  # do something else

Python embraces the concepts of EAFP (easier to ask forgiveness than permission) over LYBL (look before you leap):

EAFP

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

LYBL

Look before you leap. This coding style explicitly tests for pre-conditions before making calls or lookups. This style contrasts with the EAFP approach and is characterized by the presence of many if statements.

EAFP is also safer in multithreaded environments (though you probably wouldn't directly access a plain dictionary anyway, but that's another story).

Exceptions in Python are quite cheap, even though sometimes are used as a control statement like this 👀

Collapse
yujiri8 profile image
Ryan Westlund

I completely disagree. Panel Morava makes good points, but one I don't think he emphasizes enough is that the ambiguity is only a problem if None is a value that might be in the dictionary. If I have a dictionary scores with string player names as keys and their values as lists of ints, I know that a key's value will never be None. Honestly, there are few cases where having multiple types of values in the same dict is not a bad idea. dict.get is helpful whenever the desired behavior on a missing key is just to proceed with a default.

Collapse
cpt9m0 profile image
Ali Ayati

I think never in the title is too exaggeration