2013-02-11 16 views
9

Cóż, pytanie jest w tytule: w jaki sposób zdefiniować słownik Pythona z niezmiennymi kluczami, ale zmienne wartości? Wpadłem na to (w pythonie 2.x):Definiowanie słownika Pythona z niezmiennymi kluczami, ale zmienne wartości

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) 

, ale wydaje mi się (nieoczekiwanie) raczej niechlujny. W szczególności, czy jest to bezpieczne, czy też istnieje ryzyko zmiany/dodania niektórych kluczy, ponieważ dziedziczę z dyktatury? Dzięki.

+0

Cóż, ktoś mógłby przypuszczalnie nazwać 'dict .__ setitem __()' na instancji 'FixedDict', nie? – cdhowie

+2

Powinieneś sprawdzić, aby 'dict.update' wywoływał' __setitem__' - Chociaż, nawet jeśli tak jest, nie jestem pewien czy jest to zależne od implementacji ... – mgilson

+0

('update' omija' __setitem__'') – katrielalex

Odpowiedz

10

Zastanów proxy zamiast dict zamiast jego podklasy. Oznacza to, że dozwolone będą tylko metody, które zdefiniujesz, zamiast cofać się do implementacji 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] 

Ponadto, należy użyć formatowania ciąg zamiast + wygenerować komunikat o błędzie, ponieważ w przeciwnym razie będzie on upaść na dowolną wartość, która nie jest ciągiem.

1

Sposób blokowania dodawania nowych kluczy zależy wyłącznie od tego, dlaczego ktoś może spróbować dodać nowe klucze. W komentarzach większość metod słownikowych, które modyfikują klucze, nie przechodzi przez __setitem__, więc wywołanie .update() doda nowe klucze.

Jeśli oczekujesz, że ktoś użyje d[new_key] = v, wtedy twoja __setitem__ jest w porządku. Jeśli mogą korzystać z innych sposobów dodawania kluczy, musisz poświęcić więcej pracy. I oczywiście, zawsze mogą to wykorzystać, aby zrobić to tak:

dict.__setitem__(d, new_key, v) 

Nie można dokonać rzeczy naprawdę niezmienne w Pythonie, można tylko zatrzymać szczególnych zmian.

11

Problem z dziedziczeniem bezpośrednim od dict polega na tym, że dość trudno jest spełnić pełny kontrakt dict (np. W twoim przypadku metoda update nie będzie zachowywać się w spójny sposób).

co chcesz, jest rozszerzenie 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 

Zauważ, że oryginalny (zawinięte) DICT zostanie zmodyfikowany, jeśli nie chcesz, aby tak się stało, należy copy or deepcopy.

+0

+1 "MutableMapping" to sposób, aby przejść tutaj – katrielalex