2010-10-25 20 views
67

Quiero escribir una clase personalizada que se comporte como dict - entonces, heredo de dict.Una clase de python que actúa como dict

Mi pregunta, sin embargo, es: ¿Necesito crear un miembro privado dict en mi método __init__() ?. No veo el sentido de esto, ya que ya tengo el comportamiento dict si simplemente heredo de dict.

¿Alguien puede señalar por qué la mayoría de los fragmentos de herencia se ven como el de abajo?

class CustomDictOne(dict): 
    def __init__(self): 
     self._mydict = {} 

    # other methods follow 

En lugar de la simple ...

class CustomDictTwo(dict): 
    def __init__(self): 
     # initialize my other stuff here ... 

    # other methods follow 

En realidad, creo que sospechar la respuesta a la pregunta es por lo que los usuarios no pueden acceder directamente a su diccionario (es decir, tienen que utilizar los métodos de acceso que has proporcionado).

Sin embargo, ¿qué pasa con el operador de acceso a la matriz []? ¿Cómo podría uno implementar eso? Hasta ahora, no he visto un ejemplo que muestre cómo anular el operador [].

Entonces, si no se proporciona una función de acceso [] en la clase personalizada, ¿los métodos base heredados operarán en un diccionario diferente?

He probado el siguiente fragmento de poner a prueba mi comprensión de la herencia de Python:

class myDict(dict): 
    def __init__(self): 
     self._dict = {} 

    def add(self, id, val): 
     self._dict[id] = val 


md = myDict() 
md.add('id', 123) 
print md[id] 

tengo el siguiente error:

KeyError: < built-in function id>

Lo que está mal con el código anterior?

¿Cómo puedo corregir la clase myDict para que pueda escribir código como este?

md = myDict() 
md['id'] = 123 

[Editar]

He editado el ejemplo de código anterior para deshacerse del error tonto que hice antes de que me lancé lejos de mi escritorio. Fue un error tipográfico (debería haberlo detectado en el mensaje de error).

+23

estoy refiere a menudo como 'el programador que actúa como dict'. –

+1

posible duplicado de [Python: Cómo "perfectamente" anular un dict] (http://stackoverflow.com/questions/3387691/python-how-to-perfectly-override-a-dict) –

Respuesta

2

El problema con este trozo de código:

class myDict(dict): 
    def __init__(self): 
     self._dict = {} 

    def add(id, val): 
     self._dict[id] = val 


md = myDict() 
md.add('id', 123) 

... es que su método de 'añadir' (... y cualquier método que desea ser un miembro de una clase) debe tener una explícita 'yo' declaró como primer argumento, como:

def add(self, 'id', 23): 

para implementar la sobrecarga de operadores para acceder a los elementos de clave, busque en la docs de los métodos mágicos __getitem__ y __setitem__.

Tenga en cuenta que debido a que Python usa Duck Typing, en realidad puede no haber ninguna razón para derivar su clase dict personalizada de la clase dict del lenguaje, sin saber más acerca de lo que está tratando de hacer (por ejemplo, si necesita pasar una instancia de esta clase en algún código en algún lugar que se romperá a menos que isinstance(MyDict(), dict) == True), es mejor que solo implemente la API que hace que su clase sea lo suficientemente similar a un dictado y se detenga allí.

52

gusta esta

class CustomDictOne(dict): 
    def __init__(self,*arg,**kw): 
     super(CustomDictOne, self).__init__(*arg, **kw) 

Ahora puede utilizar las funciones incorporadas, como dict.get() como self.get().

No necesita envolver un self._dict oculto. Su clase ya es es a dict.

+0

Esto. No tiene sentido heredar de '' dict'' sin llamar primero a su constructor. – sykora

+1

Tenga en cuenta que su 'dict' heredado contiene en realidad 2 dict-instance: el 1º es el contenedor heredado, y el 2º es el dict que contiene los atributos de clase; puede evitarlo utilizando [__slots__] (https: // docs. python.org/3/reference/datamodel.html#slots). – ankostis

+1

El '__dict__' solo se crea cuando se accede por primera vez, por lo que siempre que los usuarios no intenten usarlo, está bien. '__slots__' sería bueno sin embargo. –

66
class Mapping(dict): 

    def __setitem__(self, key, item): 
     self.__dict__[key] = item 

    def __getitem__(self, key): 
     return self.__dict__[key] 

    def __repr__(self): 
     return repr(self.__dict__) 

    def __len__(self): 
     return len(self.__dict__) 

    def __delitem__(self, key): 
     del self.__dict__[key] 

    def clear(self): 
     return self.__dict__.clear() 

    def copy(self): 
     return self.__dict__.copy() 

    def has_key(self, k): 
     return k in self.__dict__ 

    def update(self, *args, **kwargs): 
     return self.__dict__.update(*args, **kwargs) 

    def keys(self): 
     return self.__dict__.keys() 

    def values(self): 
     return self.__dict__.values() 

    def items(self): 
     return self.__dict__.items() 

    def pop(self, *args): 
     return self.__dict__.pop(*args) 

    def __cmp__(self, dict_): 
     return self.__cmp__(self.__dict__, dict_) 

    def __contains__(self, item): 
     return item in self.__dict__ 

    def __iter__(self): 
     return iter(self.__dict__) 

    def __unicode__(self): 
     return unicode(repr(self.__dict__)) 


o = Mapping() 
o.foo = "bar" 
o['lumberjack'] = 'foo' 
o.update({'a': 'b'}, c=44) 
print 'lumberjack' in o 
print o 

In [187]: run mapping.py 
True 
{'a': 'b', 'lumberjack': 'foo', 'foo': 'bar', 'c': 44} 
+13

Si vas a subclasificar 'dict', entonces deberías usar el objeto mismo (usando' super') en lugar de simplemente delegar al '__dict__' de la instancia, lo que esencialmente significa que estás creando dos dicts por cada ejemplo. –

6

En aras de la exhaustividad, aquí está el enlace a la documentación mencionada por @ Björn-Pollex para la última Python 2.x (2.7.7 a partir del momento de la escritura):

Emulating Container Types

(lo siento por no usar la función de los comentarios, yo sólo no pueden hacerlo por stackoverflow.)

Cuestiones relacionadas