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
- in constructor
- in
setdefault
method - 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
Post a Comment