8

Sometimes we may need to verify the existence of a key in a dictionary and make something if the corresponding value exists.

Usually, I proceed in this way:

if key in my_dict:
    value = my_dict[key]
    # Do something

This is of course highly readable and quick, but from a logical point of view, it bothers me because the same action is performed twice in succession. It's a waste of time.

Of course, access to the value in the dictionary is amortized and is normally instantaneous. But what if this is not the case, and that we need to repeat it many times?

The first workaround I thought to was to use .get() and check if the returned value is not the default one.

value = my_dict.get(key, default=None)
if value is not None:
    # Do Something

Unfortunately, this is not very practical and it can cause problems if the key exists and its corresponding value is precisely None.

Another approach would be to use a try / except block.

try:
    value = my_dict[key]
    # Do something
except KeyError:
    pass

However, I highly doubt this is the best of ways.

Is there any other solution which is unknown to me, or should I continue using the traditional method?

Delgan
  • 18,571
  • 11
  • 90
  • 141
  • 4
    In python `try / except` most clostly fits the "ask forgiveness, not permission" paradigm. Otherwise, you need to use `.get()` with a default value that will _never_ show up as a valid value for your dictionary. Something like `float('-inf')` perhaps? – g.d.d.c Jul 07 '15 at 19:05
  • You could try using a default that's unlikely to be present in your data, like an object of the `Ellipsis` class, e.g. `my_dict.get(key, ...)`. – TigerhawkT3 Jul 07 '15 at 19:06
  • 4
    In Python, [`try`/`except` IS the way (correct, accepted, and normal) to do this](http://stackoverflow.com/a/16138864/2437514). The style associated with this approach [even has its own name](https://docs.python.org/2/glossary.html#term-eafp). – Rick Jul 07 '15 at 19:07
  • `try..except` is probably the best, most Pythonic way to do this (unless you expect a lot of failed lookups, in which case `if` would be preferable). – TigerhawkT3 Jul 07 '15 at 19:11
  • In a lot of languages, the culture is that exceptions are for errors only, not normal control flow. Python explicitly rejects that. Even the `for` loop is implemented using exceptions; it's completely normal to catch exceptions in non-error situations in Python. – user2357112 Jul 07 '15 at 19:31

3 Answers3

9

If the situations with missing values are really exceptional then the exception handling is appropriate:

try:
    value = my_dict[key]
    # Do something
except KeyError:
    pass

If both missing and present keys are to be handled then your first option is useful.

if key in my_dict:
    value = my_dict[key]
    # Do something

The use of get doesn't provide any advantage in this specific situation. In contrary, you have to find a non-conflicting value to identify the missing key condition.

dlask
  • 8,776
  • 1
  • 26
  • 30
  • Could you elaborate on the last remark? I'd say `value = my_dict.get(key, "")` is quite easy to read and less code. – Bart Friederichs Jun 05 '18 at 14:01
  • @BartFriederichs In general you cannot be sure that your default value doesn't exist in the dictionary so you could be potentially unable to distinguish between valid key and invalid key. Then you cannot treat invalid keys differently as required. – dlask Jun 05 '18 at 18:33
2

As mentioned several times, exception handling is the way to go. However, if you want to use get and None is a possible value, create a new object to act as the "not found" sentinel. (Since you've just created the object, it's impossible for it to be a valid entry in your dict. Well, not impossible, but you would really have to go out of your way to add it to the dict.)

sentinel = object()
value = my_dict.get(key, sentinel)
if value is not sentinel:
    # Do something with value
chepner
  • 497,756
  • 71
  • 530
  • 681
1
if (dict.get('key') is not None) and ('key' in dict):
    var = dict['key']

Second portion of if-statement may not be totally necessary but I have used this format in my own code and it does the job.

Emmy R
  • 124
  • 3
  • 10