2013-02-11 8 views
9

Bene, la domanda è nel titolo: come definisco un dizionario python con chiavi immutabili ma valori mutabili? Sono venuto con questo (in python 2.x):Definire un dizionario python con chiavi immutabili ma valori mutabili

class FixedDict(dict): 
    """ 
    A dictionary with a fixed set of keys 
    """ 

    def __init__(self, dictionary): 
     dict.__init__(self) 
     for key in dictionary.keys(): 
      dict.__setitem__(self, key, dictionary[key]) 

    def __setitem__(self, key, item): 
     if key not in self: 
      raise KeyError("The key '" +key+"' is not defined") 
     dict.__setitem__(self, key, item) 

, ma sembra a me (com'era prevedibile) piuttosto sciatta. In particolare, questo è sicuro o c'è il rischio di cambiare/aggiungere effettivamente alcune chiavi, dal momento che sto ereditando da Dict? Grazie.

+0

Beh, qualcuno potrebbe presumibilmente chiamare "dict .__ setitem __()" su un'istanza di 'FixedDict', no? – cdhowie

+2

Dovresti controllare che 'dict.update' chiami' __setitem__' - Sebbene, anche se così fosse, non sono sicuro che dipenda dall'implementazione ... – mgilson

+0

('update' ignora' __setitem__') – katrielalex

risposta

10

Prendere in considerazione il proxy dict anziché la sottoclasse. Ciò significa che saranno consentiti solo i metodi definiti, invece di ricorrere alle implementazioni di dict.

class FixedDict(object): 
     def __init__(self, dictionary): 
      self._dictionary = dictionary 
     def __setitem__(self, key, item): 
       if key not in self._dictionary: 
        raise KeyError("The key {} is not defined.".format(key)) 
       self._dictionary[key] = item 
     def __getitem__(self, key): 
      return self._dictionary[key] 

Inoltre, si dovrebbe utilizzare la formattazione stringa invece di + per generare il messaggio di errore, in quanto altrimenti si andrà in crash per qualsiasi valore che non è una stringa.

1

Come si impedisce a qualcuno di aggiungere nuove chiavi dipende interamente dal motivo per cui qualcuno potrebbe provare ad aggiungere nuove chiavi. Come affermano i commenti, la maggior parte dei metodi di dizionario che modificano le chiavi non passano attraverso __setitem__, quindi una chiamata .update() aggiungerà nuove chiavi bene.

Se ci si aspetta che qualcuno usi d[new_key] = v, allora il proprio __setitem__ è a posto. Se possono usare altri modi per aggiungere le chiavi, allora devi lavorare di più. E, naturalmente, si può sempre usare questo per farlo comunque:

dict.__setitem__(d, new_key, v) 

Non si possono fare le cose veramente immutabili in Python, ci si può fermare solo particolari modifiche.

11

Il problema con l'eredità diretta da dict è che è molto difficile da rispettare il contratto completo dict s'(ad esempio, nel tuo caso, update metodo non si comporterà in modo coerente).

Quello che vuoi, è quello di estendere il collections.MutableMapping:

import collections 

class FixedDict(collections.MutableMapping): 
    def __init__(self, data): 
     self.__data = data 

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

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

    def __setitem__(self, k, v): 
     if k not in self.__data: 
      raise KeyError(k) 

     self.__data[k] = v 

    def __delitem__(self, k): 
     raise NotImplementedError 

    def __getitem__(self, k): 
     return self.__data[k] 

    def __contains__(self, k): 
     return k in self.__data 

Nota che l'originale (avvolto) dict sarà modificato, se non si desidera che ciò accada, utilizzare copy or deepcopy.

+0

+1 'MutableMapping' è il modo per andare qui – katrielalex