Estoy construyendo una clase que subclasifica dict
, y anula __setitem__
. Me gustaría estar seguro de que se llamará a mi método en todas las instancias en las que se puedan establecer los elementos del diccionario.Subclassing Python dictionary para reemplazar __setitem__
he descubierto tres situaciones en Python (en este caso, 2.6.4) no llamar a mi __setitem__
método anulado cuando los valores de ajuste, y en lugar de llamadas PyDict_SetItem
directamente
- En el constructor
- En el
setdefault
método - En el método
update
Como una prueba muy 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'}
Puede ver que el método reemplazado solo se invoca al establecer los elementos de forma explícita. Para obtener Python para llamar siempre a mi método __setitem__
, he tenido que volver a implementar esos tres métodos, como esto:
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]
¿Hay otros métodos que necesito para anular, con el fin de saber que Python siempre llamar a mi método __setitem__
?
ACTUALIZACIÓN
Por sugerencia de GS, he tratado de subclases UserDict (en realidad, IterableUserDict, ya que quiero para iterar sobre las claves) como esto:
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)
Esta clase parece correcta llame a mi __setitem__
en setdefault
, pero no lo llama en update
, o cuando los datos iniciales se proporcionan al constructor.
ACTUALIZACIÓN 2
la sugerencia de Peter Hansen me llevó a mirar con más cuidado dictobject.c, y me di cuenta de que el método de actualización se podría simplificar un poco, ya que el constructor diccionario incorporado, simplemente llama a la incorporada -en el método de actualización de todos modos. Ahora se ve así:
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]
¿Funciona si subclasifica UserDict en lugar del dict normal? –
Honestamente, no me di cuenta de que UserDict todavía estaba por aquí :) Lo intentaré –
El UserDict habría sido bueno, pero desafortunadamente su método de actualización simplemente llama a update en el diccionario de datos subyacente. –