Я создаю класс, который подклассов dict
и переопределяет __setitem__
. Я хотел бы быть уверен, что мой метод будет вызываться во всех случаях, когда возможно установить элементы словаря.
Я обнаружил три ситуации, когда Python (в данном случае 2.6.4) не вызывает мой переопределенный метод __setitem__
при установке значений, а вместо этого вызывает PyDict_SetItem
напрямую
- В конструкторе
- В методе
setdefault
- В методе
update
Как очень простой тест:
class MyDict(dict):
def __setitem__(self, key, value):
print "Here"
super(MyDict, self).__setitem__(key, str(value).upper())
>>> a = MyDict(abc=123)
>>> a['def'] = 234
Here
>>> a.update({'ghi': 345})
>>> a.setdefault('jkl', 456)
456
>>> print a
{'jkl': 456, 'abc': 123, 'ghi': 345, 'def': '234'}
Вы видите, что переопределенный метод вызывается только при явной установке элементов. Чтобы Python всегда вызывал мой метод __setitem__
, мне пришлось переопределить эти три метода, например:
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 at most 1 arguments, got %d" % len(args))
other = dict(args[0])
for key in other:
self[key] = other[key]
for key in kwargs:
self[key] = kwargs[key]
def setdefault(self, key, value=None):
if key not in self:
self[key] = value
return self[key]
Существуют ли другие методы, которые мне нужно переопределить, чтобы знать, что Python будет всегда вызывать мой __setitem__
метод?
UPDATE
Согласно предложению gs, я попытался создать подкласс UserDict (фактически, IterableUserDict, так как я хочу перебирать ключи) следующим образом:
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)
Этот класс, кажется, правильно вызывает my __setitem__
на setdefault
, но не вызывает его на update
или когда начальные данные передаются конструктору.
ОБНОВЛЕНИЕ 2
Предложение Питера Хансена заставило меня более внимательно посмотреть на dictobject.c, и я понял, что метод обновления можно немного упростить, поскольку встроенный конструктор словаря просто вызывает встроенный метод обновления в любом случае. Теперь это выглядит так:
def update(self, *args, **kwargs):
if len(args) > 1:
raise TypeError("update expected at most 1 arguments, got %d" % len(args))
other = dict(*args, **kwargs)
for key in other:
self[key] = other[key]