Subclassing Python dictionary to override __setitem__ -


i building class subclasses dict, , overrides __setitem__. method called in instances dictionary items possibly set.

i have discovered 3 situations python (in case, 2.6.4) not call overridden __setitem__ method when setting values, , instead calls pydict_setitem directly

  1. in constructor
  2. in setdefault method
  3. in update method

as simple test:

class mydict(dict):     def __setitem__(self, key, value):         print "here"         super(mydict, self).__setitem__(key, str(value).upper())  >>> = mydict(abc=123) >>> a['def'] = 234 here >>> a.update({'ghi': 345}) >>> a.setdefault('jkl', 456) 456 >>> print {'jkl': 456, 'abc': 123, 'ghi': 345, 'def': '234'} 

you can see overridden method called when setting items explicitly. python call __setitem__ method, have had reimplement 3 methods, this:

class myupdatedict(dict):     def __init__(self, *args, **kwargs):         self.update(*args, **kwargs)      def __setitem__(self, key, value):         print "here"         super(myupdatedict, self).__setitem__(key, value)      def update(self, *args, **kwargs):         if args:             if len(args) > 1:                 raise typeerror("update expected @ 1 arguments, got %d" % len(args))             other = dict(args[0])             key in other:                 self[key] = other[key]         key in kwargs:             self[key] = kwargs[key]      def setdefault(self, key, value=none):         if key not in self:             self[key] = value         return self[key] 

are there other methods need override, in order know python always call __setitem__ method?

update

per gs's suggestion, i've tried subclassing userdict (actually, iterableuserdict, since want iterate on keys) this:

from userdict import *; class myuserdict(iterableuserdict):     def __init__(self, *args, **kwargs):         userdict.__init__(self,*args,**kwargs)      def __setitem__(self, key, value):         print "here"         userdict.__setitem__(self,key, value) 

this class seems correctly call __setitem__ on setdefault, doesn't call on update, or when initial data provided constructor.

update 2

peter hansen's suggestion got me more @ dictobject.c, , realised update method simplified bit, since built-in dictionary constructor calls built-in update method anyway. looks this:

def update(self, *args, **kwargs):     if len(args) > 1:         raise typeerror("update expected @ 1 arguments, got %d" % len(args))     other = dict(*args, **kwargs)     key in other:         self[key] = other[key] 

i'm answering own question, since decided do want subclass dict, rather creating new mapping class, , userdict still defers underlying dict object in cases, rather using provided __setitem__.

after reading , re-reading python 2.6.4 source (mostly objects/dictobject.c, grepped eveywhere else see various methods used,) understanding following code is sufficient have __setitem__ called every time object changed, , otherwise behave python dict:

peter hansen's suggestion got me more @ dictobject.c, , realised update method in original answer simplified bit, since built-in dictionary constructor calls built-in update method anyway. second update in answer has been added code below (by helpful person ;-).

class myupdatedict(dict):     def __init__(self, *args, **kwargs):         self.update(*args, **kwargs)      def __setitem__(self, key, value):         # optional processing here         super(myupdatedict, self).__setitem__(key, value)      def update(self, *args, **kwargs):         if args:             if len(args) > 1:                 raise typeerror("update expected @ 1 arguments, "                                 "got %d" % len(args))             other = dict(args[0])             key in other:                 self[key] = other[key]         key in kwargs:             self[key] = kwargs[key]      def setdefault(self, key, value=none):         if key not in self:             self[key] = value         return self[key] 

i've tested code:

def test_updates(dictish):     dictish['abc'] = 123     dictish.update({'def': 234})     dictish.update(red=1, blue=2)     dictish.update([('orange', 3), ('green',4)])     dictish.update({'hello': 'kitty'}, black='white')     dictish.update({'yellow': 5}, yellow=6)     dictish.setdefault('brown',7)     dictish.setdefault('pink')     try:         dictish.update({'gold': 8}, [('purple', 9)], silver=10)     except typeerror:         pass     else:         raise runtimeexception("error did not occur planned")  python_dict = dict([('b',2),('c',3)],a=1) test_updates(python_dict)  my_dict = myupdatedict([('b',2),('c',3)],a=1) test_updates(my_dict) 

and passes. other implementations i've tried have failed @ point. i'll still accept answers show me i've missed something, otherwise, i'm ticking checkmark beside 1 in couple of days, , calling right answer :)


Comments

Popular posts from this blog

c++ - Convert big endian to little endian when reading from a binary file -

C#: Application without a window or taskbar item (background app) that can still use Console.WriteLine() -

unicode - Are email addresses allowed to contain non-alphanumeric characters? -