2012-02-17 110 views
5

由於在這裏的話題很多問題,這樣證明的子集的自己的字典視圖,以字典的片爲一個很常見的任務,具有相當不錯的解決方案:Python中創建字典

{k:v for k,v in dict.viewitems() if some_test(k,v)} 

但它創建了一個新的字典,並帶有自己的映射。對於許多操作,只需擁有原始字典的不可變視圖(即不支持視圖上的分配或刪除操作)將會很好。實現這種類型可能很容易,但是增加地方實用程序類並不好。

所以,我的問題是:是否有內置的方式獲得這樣的「子集視圖」?或者是否有第三方庫(最好通過PyPi提供)可以很好地實現此類實用程序?

+1

我建議的「不可改變的觀點」在一本字典正是你與你的示例代碼獲取...因爲缺席使得字典的單獨副本,我不知道你將如何讓「不可變的」部分工作。 – larsks 2012-02-17 14:07:46

+0

@larsks:它可能根本不支持分配。 – Marcin 2012-02-17 14:16:36

+1

@larsks:我認爲,通過「不可變視圖」,OP意味着視圖對象本身沒有方法來改變字典(例如pop),並且對視圖中包含的字典的任何更改都立即可見。當然,它在「深層」意義上並不是不可變的 - 也就是說,如果您執行my_view [some_key] .append(12),那麼當然對應於12的值將被修改。 – 2012-02-17 14:17:58

回答

4

這是很容易實現:

from collections import Mapping 
class FilteredItems(Mapping): 
    def __init__(self, source, filter): 
     self.source = source 
     self.p = filter 

    def __getitem__(self, key): 
     x = self.source[key] 
     if self.p(key,x): 
      return key,x 
     else: 
      raise KeyError(key) 


d2 = FilteredItems(d, some_test) 
+0

+1使用映射基類 – theheadofabroom 2012-02-17 14:41:32

+2

是的,這很容易實現,而且看起來不錯,但正如所指出的,許多本地實現和名稱並不是一件好事。 – Marcin 2012-02-17 14:58:41

2

要澄清的語義,你想的是這樣的:?

class FilteredDictView: 
    def __init__(self, base_dict, test): 
     self._base_dict = base_dict 
     self._test = test 
    def __getitem__(self, key): 
     value = self._base_dict[key] # might throw KeyError 
     if not self._test(key,value): 
      throw KeyError(key) 
     return value 
    # ... implement remaining dict-like-methods ... 

如果是這樣,那麼我不知道任何這樣的第三方類。如果你想讓實現剩下的方法變得容易一點,你可以看看使用「UserDict」作爲基類,它基本上只是dict的包裝器(「UserDict.data」屬性用於存儲包裝的字典) 。

+0

是的,正是這樣的事情。 – Marcin 2012-02-17 14:18:25

4

似乎沒有內建的方式來獲得字典的視圖。最簡單的解決方法似乎是Jochen的方法。我適應了他的代碼稍微讓它爲我的目的工作:在各方面

from collections import MutableMapping 

class DictView(MutableMapping): 
    def __init__(self, source, valid_keys): 
     self.source, self.valid_keys = source, valid_keys 

    def __getitem__(self, key): 
     if key in self.valid_keys: 
      return self.source[key] 
     else: 
      raise KeyError(key) 

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

    def __iter__(self): 
     for key in self.valid_keys: 
      yield key 

    def __setitem__(self, key, value): 
     if key in self.valid_keys: 
      self.source[key] = value 
     else: 
      raise KeyError(key) 

    def __delitem__(self, key): 
     self.valid_keys.remove(key) 

d = dict(a=1, b=2, c=3) 
valid_keys = ['a', 'c'] 
d2 = DictView(d, valid_keys) 
d2['a'] = -1 # overwrite element 'a' in source dictionary 
print d # prints {'a': -1, 'c': 3, 'b': 2} 

所以d2行爲就像一本字典,除了印刷,由於不同__repr__()方法。繼承dict得到__repr__()將需要重新實現每一種方法,如collections.OrderedDict所做的那樣。如果只需要只讀視圖,則可以繼承collections.Mapping並保存__setitem__()__delitem__()的實現。我發現DictView用於從self.__dict__中選擇參數並將它們以緊湊形式傳遞。