Jj Del Carpio

Jj's web stream

KeyError from dict where Key is present - Python

I had this problem at work today. I had this snippet of Python code:

self.pop(key, None)

Which raised KeyError. I inspected and I was inside an OrderedDict subclass, so inside the pop() method implementation I had this code:

if key in self:
  del self[key]

Which was the originator of the exception raised. Took me a while to find out what the problem was until I noticed that the instance I was working on was not operating on the class where the pop() call was made, but on a subclass that reimplemented __getitem__. This made me open my eyes and spot the error.

This was my situation. I was looking at the code inside the delete_key method, which is defined under MyBaseDict, but I was on an instance of MyOtherDict. Look at the following code:

class MyBaseDict(dict):
    def delete_key(self, key):
        del self[key]


class MyOtherDict(MyBaseDict):
    def __init__(self, base, *args, **kwargs):
        self.base = base
        super(MyOtherDict, self).__init__(*args, **kwargs)

    def __getitem__(self, key):
        return self.base[key]

my_od = MyBaseOd()
my_od['a'] = 1
my_od['b'] = 2
assert my_od['b'] == 2
other_od = MyOtherOd(my_od)
assert other_od['b'] == 2
del other_od['b']  # Raises KeyError

At first sight it makes no sense that after I asserted that the key 'b' exists I get a KeyError trying to delete it! But what is happening is that the del statement is calling an internal implementation of the base class, dict in this case. Which attempts to use an internal implementation of __getdict__ and not the user defined one.

The proper way to make a subclass of dict in Python requires you to subclass collections.MutableMapping. That will ensure that your del statement uses the user defined dict protocol methods.

from collections import MutableMapping

class MyBaseDict(MutableMapping):
    ....

With the original inheritance change from dict to MutableMapping the above script now works.

Jj Avatar of Jj

Reply or react to this post via Webmentions