Je construis une classe qui sous-classe dict
et remplace __setitem__
. Je voudrais être certain que ma méthode sera appelée dans tous les cas où des éléments du dictionnaire pourraient être définis.
J'ai découvert trois situations dans lesquelles Python (dans ce cas, 2.6.4) n'appelle pas ma fonction surchargée __setitem__
lors de la définition des valeurs, et appelle à la place la méthode PyDict_SetItem
directement
- Dans le constructeur
- Dans le
setdefault
méthode - Dans le
update
méthode
Il s'agit d'un test très simple :
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'}
Vous pouvez voir que la méthode surchargée n'est appelée que lorsque les éléments sont définis explicitement. Pour que Python appelle toujours ma méthode __setitem__
j'ai dû réimplémenter ces trois méthodes, comme ceci :
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]
Existe-t-il d'autres méthodes que je dois surcharger, afin de savoir que Python va toujours appeler mon __setitem__
méthode ?
UPDATE
Selon la suggestion de gs, j'ai essayé de sous-classer UserDict (en fait, IterableUserDict, puisque je veux itérer sur les clés) comme ceci :
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)
Cette classe semble appeler correctement mon __setitem__
sur setdefault
mais il ne l'appelle pas sur update
ou lorsque les données initiales sont fournies au constructeur.
MISE À JOUR 2
La suggestion de Peter Hansen m'a poussé à regarder plus attentivement le fichier dictobject.c, et j'ai réalisé que la méthode de mise à jour pouvait être simplifiée un peu, puisque le constructeur du dictionnaire intégré appelle simplement la méthode de mise à jour intégrée de toute façon. Cela ressemble maintenant à ceci :
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]