434

Is there any other way to delete an item in a dictionary only if the given key exists, other than:

if key in mydict:
    del mydict[key]

The scenario is that I'm given a collection of keys to be removed from a given dictionary, but I am not certain if all of them exist in the dictionary. Just in case I miss a more efficient solution.

Simon Hughes
  • 4,739
  • 4
  • 19
  • 12
  • 5
    @user1929959 Nope, that question wants a copy of the dictionary, and doesn't care about hte case that the key is not in the input. – phihag Mar 14 '13 at 13:52

3 Answers3

916

You can use dict.pop:

 mydict.pop("key", None)

Note that if the second argument, i.e. None is not given, KeyError is raised if the key is not in the dictionary. Providing the second argument prevents the conditional exception.

Asclepius
  • 57,944
  • 17
  • 167
  • 143
Adem Öztaş
  • 20,457
  • 4
  • 34
  • 42
  • 85
    For reference, the second argument to `.pop()` is what it returns if the key is not found. – OregonTrail Jul 23 '15 at 01:05
  • 1
    Does this only do 1 lookup? – David Callanan Nov 19 '17 at 18:34
  • 1
    So, just for curiosity, what is the default value of the second argument in the source code? – Carlos Pinzón Jul 19 '21 at 11:14
  • 3
    @CarlosPinzón There isn't one. See [the docs](https://docs.python.org/3/library/stdtypes.html#dict.pop). `D.pop(key[, default])` "If `key` is not found, `default` is returned if given, otherwise `KeyError` is raised". – Jonathon Reinhart Dec 08 '21 at 05:50
  • 1
    @GirishGupta: I doubt it. Python `dict` is written in C. I would be very surprised if not optimised for single lookup. Ref: https://github.com/python/cpython/blob/main/Objects/dictobject.c#L2206 – kevinarpe Sep 08 '22 at 05:33
  • **`.pop()` does a single lookup, as seen in the Python source code:** https://github.com/python/cpython/blob/34e93d3998bab8acd651c50724eb1977f4860a08/Objects/dictobject.c#L2231 – Mitch McMabers Jun 17 '23 at 14:51
28

There is also:

try:
    del mydict[key]
except KeyError:
    pass

This only does 1 lookup instead of 2. However, except clauses are expensive, so if you end up hitting the except clause frequently, this will probably be less efficient than what you already have.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • that is my fear. I wasn't sure about the cost of two look-ups per deletion vs. that of using try-except. – Simon Hughes Mar 14 '13 at 13:55
  • 4
    @SimonHughes -- See [this answer](http://programmers.stackexchange.com/questions/175655/python-forgiveness-vs-permission-and-duck-typing/175663#175663) on programmers ... – mgilson Mar 14 '13 at 13:57
  • 2
    This is called the [eaiser to ask for forgiveness than permission](https://docs.python.org/3/glossary.html#term-eafp) (EAFP) Python style. – akki Jul 22 '16 at 16:55
  • 2
    And EAFP is the recommended approach in Python. I would like to see the evidence and rationale for the statement "except clauses are expensive." – Bobort Oct 26 '16 at 14:44
  • 2
    @Bobort -- This has been a pretty well explained in a number of places. See [this answer](http://softwareengineering.stackexchange.com/questions/175655/python-forgiveness-vs-permission-and-duck-typing/175663#175663) on programmers or [this response](https://mail.python.org/pipermail/tutor/2011-January/081143.html) on the python mailing list. In short, setting up the try/except block is cheap, but handling the exception is less cheap. – mgilson Oct 26 '16 at 16:25
6

Approach: calculate keys to remove, mutate dict

Let's call keys the list/iterator of keys that you are given to remove. I'd do this:

keys_to_remove = set(keys).intersection(set(mydict.keys()))
for key in keys_to_remove:
    del mydict[key]

You calculate up front all the affected items and operate on them.

Approach: calculate keys to keep, make new dict with those keys

I prefer to create a new dictionary over mutating an existing one, so I would probably also consider this:

keys_to_keep = set(mydict.keys()) - set(keys)
new_dict = {k: v for k, v in mydict.iteritems() if k in keys_to_keep}

or:

keys_to_keep = set(mydict.keys()) - set(keys)
new_dict = {k: mydict[k] for k in keys_to_keep}
hughdbrown
  • 47,733
  • 20
  • 85
  • 108