Magic of dictionaries in Python

January 30, 2017 by Paweł Biernacki

Python supports well known data types, like lists and dictionaries that are easy to use. But can you cheat interpreter to use easier semantics to access dict data?

PEP 484 introduced type hinting which was first implemented in Python 3.5. We can use it to instruct our IDE to help us with code completion. Unfortunately it’s a bit intrusive and requires a lot of additional typing, it’s also unavailable in older, still supported, releases.

We can use a dictionary and a few magic methods to help ourselves. Implementation in Python 3 is easier, because we have access to abstract type MutableMapping that expects implementation of few methods and adds many others without any additional code (by inheriting from other classes). The complete clone in Python 2 will require a lot more code, but the basic version can be created very easily.

In Python 3:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from collections import MutableMapping
from functools import partial
from operator import add


class MagicDict(MutableMapping):
    def __getitem__(self, key):
        return self.__dict__[key]

    def __delitem__(self, key):
        del self.__dict__[key]

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __iter__(self):
        return iter(self.__dict__)

    def __len__(self):
        return len(self.__dict__)


if __name__ == '__main__':
    md = MagicDict()
    md.data = 2+2*2
    md.add_two = partial(add, 2)

    print(md.add_two(md.data))

    for k, v in md.items():
        print("{} => {}".format(k, v))

will output:

1
2
3
8
data => 6
add_two => functools.partial(<built-in function add>, 2)


In Python 2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class MagicDict(object):
    def __init__(self):
        super(MagicDict, self).__setattr__('__dict__', {})

    def __setattr__(self, key, value):
        self.__dict__[key] = value

    def __getattr__(self, key):
        return self.__dict__[key]

    def __iter__(self):
        return iter(self.__dict__)

    def values(self):
        return self.__dict__.values()

    def iteritems(self):
        return self.__dict__.iteritems()

if __name__ == '__main__':
    md = MagicDict()
    md.data = 2+2*2
    md.add_two = lambda x: x + 2

    print(md.add_two(md.data))

    for k, v in md.iteritems():
        print("{} => {}".format(k, v))

will output:

1
2
3
    8
    data = > 6
    add_two = > <function <lambda> at 0x10d4f8410>
Posted in: Python