Subclass dict __getitem__ while maintaining original class type

Viewed 360

I am trying to subclass a python dictionary __getitem__ method while maintaining the original class type when accessing the hash. No answer in properly subclassing or perfect overriding seemed to address this. For example,

class MyDict(dict):
    def __init__(self, data=None):
        if not data:
            data = {}
        dict.__init__(self, data)

    def __getitem__(self, key):
        if key == 'b':
            print('Found "b"')

        return dict.__getitem__(self, key)

shallow_dict = MyDict(data={
    'b': 'value'
})

# prints Found "b", as expected
x = shallow_dict['b']

deep_dict = MyDict(data={
    'a': {
        'b': 'value'
    }
})

# prints nothing
x = deep_dict['a']['b']

This happens because when we're accessing ['b'] we're actually accessing a dict, not MyDict anymore. So I tried to solve this by copying the content into a new object:

def __getitem__(self, key):
    data = dict.__getitem__(self, key)
    if key == 'b':
        print('Found "b"')

    if isinstance(data, dict):
        return MyDict(data)
    return data

However this solution lead to a new problem while writing content into the hash, because I'm returning a copy and not a reference:

deep_dict['a']['b'] = 'other value'

# prints 'value'
print(deep_dict['a']['b'])

Any suggestions on how to properly maintain the type, since copying had this side effect?

1 Answers

How about your MyDict just be a proxy for the dict:

class MyDict(object):
    def __init__(self, data={}):
        self.data = data

    def __getitem__(self, key):
        if key == 'b':
            print('Found "b"')
        return MyDict(self.data.__getitem__(key))

    def __setitem__(self, key, value):
        return self.data.__setitem__(key, value)

    def __repr__(self):
        return self.data.__repr__()

    # add more __magic__ methods as you wish

shallow_dict = MyDict({
    'b': 'value'
})

x = shallow_dict['b']


deep_dict = MyDict({
    'a': {
        'b': 'value'
    }
})

x = deep_dict['a']['b']

# assignment
deep_dict['a']['a'] = {'b': 'here'}
deep_dict['a']['a']['b']
print(deep_dict)

OUTPUT:

Found "b"
Found "b"
Found "b"
{'a': {'b': 'value', 'a': {'b': 'here'}}}

As you can see when you get the self.data inside __getitem__ it just pass the result of self.data.__getitem__ by reference to a new MyDict object.

Related