overriding __getitem__ for a subclass of dict

Steve Howell showell30 at yahoo.com
Tue Nov 17 05:53:50 CET 2009

On Nov 16, 5:46 pm, Steven D'Aprano
<ste... at REMOVE.THIS.cybersource.com.au> wrote:
> On Mon, 16 Nov 2009 10:32:19 -0800, Steve Howell wrote:
> > Actually, the __getitem__ workaround that I proposed earlier only works
> > on subclasses of dict, not dict themselves.  So given a pure dictionary
> > object, it is impossible to hook into attribute lookups after
> > instantiation in debugging/tracing code.
> If you have written your application to avoid unnecessary isinstance and
> type checks, i.e. to use duck-typing, then a technique you might find
> useful is delegation.
> class DebuggingDict(object):
>     def __init__(self, dict_to_wrap, hook=None):
>         self.__dict__['_d'] = dict_to_wrap
>         self.__dict__['getitem_hook'] = hook
>     def __getattr__(self, name):
>         return getattr(self._d, name)
>     def __setattr__(self, name, value):
>         setattr(self._d, name, value)
>     def __getitem__(self, key):
>         if self.getitem_hook is not None:
>             self.getitem_hook(self, key)
>         return self._d[key]
> And in use:
> >>> def hook(self, key):
> ...     print "Looking for key", key
> ...>>> d = DebuggingDict({1:'a', 2: 'b'}, hook)
> >>> d[1]
> Looking for key 1
> 'a'>>> d[2]
> Looking for key 2
> 'b'

Yep, this basically resembles the approach that I originally took for
the broader problem, which was that I wanted to see how a third party
library (the Django templating system) was accessing dictionaries that
referred to objects that my tracing code did not create.  Although I
did not instantiate the original objects, I did have the ability to
substitute masquerade objects for the original objects before passing
them along, and my code for the masquerading objects was similar in
spirit to your DebuggingDict.  It actually worked pretty well, except
that eventually my masquerade objects went off to places where I was
not fully able to maintain the illusion of being the original object.
My original post on this thread sheds some light on what I'm doing,
but basically I was trying to masquerade down the whole tree of calls
from Django, which worked fine as long as Django was trying first to
access my objects like dictionaries, which it always does first when
rendering template variables, but all bets are off after that (custom
filters, etc.).

Eventually, I realized that it was easier to just monkeypatch Django
while I was in test mode to get a more direct hook into the behavior I
was trying to monitor, and then I didn't need to bother with
overriding __getitem__ or creating complicated wrapper objects.  I
wrote about it here if anybody is morbidly interested:


More information about the Python-list mailing list