2013-02-11 41 views
9

問題在於:如何定義帶有不可變鍵值但可變值的Python字典?我想出了這個(在蟒蛇2.x):定義一個帶有不可變鍵值但可變值的Python字典

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) 

但它在我看來(不出意料),而不是馬虎。特別是,這是安全的還是有實際更改/添加一些鍵的風險,因爲我是從字典繼承? 謝謝。

+0

那麼,有人可能會在'FixedDict'的實例上調用'dict .__ setitem __()',不是嗎? – cdhowie 2013-02-11 16:28:48

+2

你應該檢查以確保'dict.update'調用'__setitem__' - 雖然,即使它確實,我不確定這是否與實現有關... – mgilson 2013-02-11 16:28:57

+0

('update'繞過'__setitem__' ) – katrielalex 2013-02-11 16:32:15

回答

10

考慮代理dict而不是繼承它的子類。這意味着只有你定義的方法纔會被允許,而不是回到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] 

此外,你應該使用字符串格式化而不是+產生錯誤消息,因爲否則會崩潰的,這不是一個字符串的任何值。

1

如何阻止某人添加新密鑰完全取決於爲什麼有人可能會嘗試添加新密鑰。正如註釋所述,大多數修改密鑰的字典方法都不會通過__setitem__,因此.update()調用會很好地添加新密鑰。

如果您只希望有人使用d[new_key] = v,那麼您的__setitem__是好的。如果他們可能使用其他方式添加密鑰,那麼你必須投入更多的工作。 ,當然,他們可以隨時使用這個反正做到這一點:

dict.__setitem__(d, new_key, v) 

你不能讓事情在Python真正一成不變的,只可以停止特定的變化。

11

dict直接繼承的問題是,它很難遵守完整的dict的合同(例如,在您的情況下,update方法不會以一致的方式運行)。

你想要什麼,是延長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 

注意,原來的(包裹)字典將被修改,如果你不希望這種情況發生,使用copy or deepcopy

+0

+1'MutableMapping'是要走的路 – katrielalex 2013-02-12 10:33:54