2012-05-25 8 views
9

Vorrei creare una struttura dati che si comporti come un dizionario con una funzionalità aggiunta che tenga traccia di quali chiavi sono state "consumate". Si noti che non posso semplicemente inserire i valori mentre vengono riutilizzati.Dizionario Python con memoria delle chiavi a cui è stato effettuato l'accesso?

La struttura dovrebbe sostenere questi tre casi, cioè contrassegnare la chiave come consumato quando vi si accede come:

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

Questo è quello che ho scritto:

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) 

Come io non sono molto familiare con l'interno di dict, c'è un modo migliore per raggiungere questo risultato?

+1

quanto spesso si usa 'unused_keys()' ? se hai decorato il setter per aggiungere le chiavi a un set e getter per provare a rimuovere le chiavi da questo set, potrebbe avere una migliore perfomance - non sono sicuro della parte ** elegance **, pensato – Aprillion

+2

A parte: perché "unused_keys" deve restituire un elenco? Non ha ordini intrinseci, quindi ha senso restituire un set. –

+0

@Thomas K: Per motivi di simmetria, 'keys' restituisce una lista. – badzil

risposta

4

Ecco una soluzione che riassume tutto all'interno di un metaclasse. Non sono sicuro se questo è davvero più eleganti, ma fornisce una certa quantità di incapsulamento si dovrebbe cambiare idea su come memorizzare le chiavi utilizzate:

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

Mi piace l'utilizzo di una metaclasse per limitare che consente una configurazione dinamica di quali metodi sono considerati "consumatori". – badzil

+0

Sono d'accordo con il commento di @ badzil e penso che forse dovrebbe andare anche oltre e consentire ai suoi clienti di definire o sovrascrivere quali metodi sono considerati consumatori - a funzionalità che credo possa essere facilmente aggiunta. – martineau

Problemi correlati