Я создаю класс, который подклассы 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
Per gs suggestion, я пробовал подклассирование 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)
Этот класс, по-видимому, правильно вызывает мой __setitem__
на setdefault
, но он не вызывает его на update
или когда исходные данные предоставляются конструктору.
ОБНОВЛЕНИЕ 2
Предложение Peter Hansen заставило меня более внимательно посмотреть на 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]