2012-05-25 7 views
9

Me gustaría crear una estructura de datos que se comporte como un diccionario con una funcionalidad añadida que es hacer un seguimiento de qué claves se han "consumido". Tenga en cuenta que no puedo mostrar los valores cuando se vuelven a usar.Diccionario de Python con memoria de teclas a las que se accedió?

La estructura debe soportar estos tres casos, es decir, marcar la clave cuando se consumen cuando se accede como:

if key in d: 
    ... 
d[key] 
d.get(key) 

Esto es lo que he escrito:

class DictWithMemory(dict): 

    def __init__(self, *args, **kwargs): 
     self.memory = set() 
     return super(DictWithMemory, self).__init__(*args, **kwargs) 

    def __getitem__(self, key): 
     self.memory.add(key) 
     return super(DictWithMemory, self).__getitem__(key) 

    def __contains__(self, key): 
     self.memory.add(key) 
     return super(DictWithMemory, self).__contains__(key) 

    def get(self, key, d=None): 
     self.memory.add(key) 
     return super(DictWithMemory, self).get(key, d) 

    def unused_keys(self): 
     """ 
     Returns the list of unused keys. 
     """ 
     return set(self.keys()).difference(self.memory) 

Como no estoy muy familiarizado con las partes internas de Dict, ¿hay una mejor manera de lograr este resultado?

+1

¿con qué frecuencia usar 'unused_keys()' ? si decoró al colocador para agregar claves a un conjunto y getter para tratar de eliminar las claves de este conjunto, podría tener un mejor rendimiento - no estoy seguro de la parte ** elegance **, pensó – Aprillion

+2

A un lado: ¿por qué 'unused_keys' devuelve un ¿lista? No tiene un orden intrínseco, por lo que tiene sentido que devuelva un conjunto. –

+0

@Thomas K: En aras de la simetría, 'keys' devuelve una lista. – badzil

Respuesta

4

Aquí hay una solución que abstrae todo dentro de una metaclase. No estoy seguro de si esto es realmente más elegante, pero proporciona una cierta cantidad de encapsulación debe cambiar de opinión acerca de cómo almacenar las claves utilizadas:

class KeyRememberer(type): 

    def __new__(meta, classname, bases, classDict): 
     cls = type.__new__(meta, classname, bases, classDict) 

     # Define init that creates the set of remembered keys 
     def __init__(self, *args, **kwargs): 
      self.memory = set() 
      return super(cls, self).__init__(*args, **kwargs) 
     cls.__init__ = __init__ 

     # Decorator that stores a requested key in the cache 
     def remember(f): 
      def _(self, key, *args, **kwargs): 
       self.memory.add(key) 
       return f(self, key, *args, **kwargs) 
      return _ 

     # Apply the decorator to each of the default implementations 
     for method_name in [ '__getitem__', '__contains__', 'get' ]: 
      m = getattr(cls, method_name) 
      setattr(cls, method_name, remember(m)) 

     return cls 


class DictWithMemory(dict): 

    # A metaclass that ensures the object 
    # has a set called 'memory' as an attribute, 
    # which is updated on each call to __getitem__, 
    # __contains__, or get. 
    __metaclass__ = KeyRememberer 

    def unused_keys(self): 
     """ 
     Returns the list of unused keys. 
     """ 
     print "Used", self.memory 
     return list(set(super(DictWithMemory, 
           self).keys()).difference(self.memory)) 
+1

Me gusta el uso de una metaclase para limitar que permita una configuración dinámica de qué métodos se consideran "consumidores". – badzil

+0

Estoy de acuerdo con el comentario de @ badzil y creo que tal vez debería ir más allá y permitir que sus clientes definan o anulen qué métodos se consideran consumidores: en la función creo que podría agregarse fácilmente. – martineau

Cuestiones relacionadas