2012-02-17 8 views
5

Como las muchas preguntas sobre el tema aquí en atestiguan SO, tomando una rebanada de un diccionario es una tarea bastante común, con una solución bastante buena:Python crear propio punto de vista dict del subconjunto de diccionario

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

Pero eso crea un nuevo diccionario, con sus propias asignaciones. Para muchas operaciones, sería bueno tener una vista inmutable del dict original (es decir, no admite operaciones de asignación o eliminación en la vista). La implementación de dicho tipo es probablemente fácil, pero no es bueno tener una proliferación de clases de servicios locales.

Entonces, mi pregunta es: ¿existe una forma incorporada de obtener una "vista de subconjunto"? ¿O hay una biblioteca de terceros (preferiblemente disponible a través de PyPi) que proporciona una buena implementación de dicha utilidad?

+1

Sugeriría que una "vista inmutable" en un diccionario es exactamente lo que obtiene con su código de ejemplo ... porque al no hacer una copia separada del diccionario, no estoy seguro de cómo va a hacer el trabajo parcial "inmutable". – larsks

+0

@larsks: Simplemente no podría admitir la asignación. – Marcin

+1

@larsks: Supongo que por "vista inmutable" el OP significa que el objeto de vista en sí no tiene métodos para mutar el diccionario (por ejemplo, pop), y que cualquier cambio en el dict envuelto es visible inmediatamente en la vista. Por supuesto, no es inmutable en un sentido "profundo", es decir, si haces my_view [some_key] .append (12), entonces, por supuesto, se modificará el valor correspondiente a 12. –

Respuesta

4

Esto es bastante fácil de implementar:

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 para usar la clase base de asignación – theheadofabroom

+2

Sí, esto es bastante fácil de implementar, y esto se ve bien, pero como se señaló, muchas implementaciones locales y nombres no son una gran cosa. – Marcin

2

Para aclarar la semántica, que está pensando en algo como esto :?

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 ... 

Si es así, entonces no sé de ninguna clase de terceros. Si desea hacer que la implementación de los métodos restantes sea un poco más fácil, podría usar "UserDict" como clase base, que básicamente es solo un contenedor para dict (el atributo "UserDict.data" se usa para almacenar el dict envuelto) .

+0

Sí, exactamente ese tipo de cosas. – Marcin

4

Parece que no hay una forma integrada de obtener una vista en un diccionario. La solución más fácil parece ser el enfoque de Jochen. He adaptado su código un poco para hacer que funcione para mis propósitos:

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} 

Así d2 se comporta como un diccionario en todos los aspectos, excepto para la impresión, debido a la diferente __repr__() método. Heredar de dict para obtener __repr__() requeriría la reimplementación de todos y cada uno de los métodos, como se hace para collections.OrderedDict. Si solo desea una vista de solo lectura, puede heredar de collections.Mapping y guardar la implementación de __setitem__() y __delitem__(). Encuentro DictView útil para seleccionar parámetros de self.__dict__ y pasarlos en una forma compacta.

Cuestiones relacionadas