Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python create own dict view of subset of dictionary

As the many questions on the topic here on SO attest, taking a slice of a dictionary is a pretty common task, with a fairly nice solution:

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

But that creates a new dictionary, with its own mappings. For many operations, it would be nice to just have an immutable view of the original dict (i.e. it does not support assignment or deletion operations on the view). Implementing such a type is probably easy, but it's not good to have a proliferation of local utility classes.

So, my question is: is there a built-in way of obtaining such a "subset view"? Or is there a third-party library (preferably available via PyPi) that provides a good implementation of such a utility?

like image 206
Marcin Avatar asked Feb 17 '12 13:02

Marcin


People also ask

How do you subset a dictionary in python?

Use a dictionary comprehension to take a subset of a dictionary. Use the syntax {key: value for key, value in dict. items() if condition} to create a new dictionary containing each key: value pair in dict for which condition is True .

Is dict () the same as {}?

tl;dr. With CPython 2.7, using dict() to create dictionaries takes up to 6 times longer and involves more memory allocation operations than the literal syntax. Use {} to create dictionaries, especially if you are pre-populating them, unless the literal syntax does not work for your case.

Can a dict contain a dict?

A dictionary can contain dictionaries, this is called nested dictionaries.

How do you slice a dictionary in python?

To slice a dictionary, you can use dictionary comprehension. In Python, dictionaries are a collection of key/value pairs separated by commas. When working with dictionaries, it can be useful to be able to easily access certain elements.


2 Answers

There seems to be no builtin way to obtain a view into a dictionary. The easiest workaround appears to be Jochen's approach. I adapted his code slightly to make it work for my purposes:

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}

So d2 behaves like a dictionary in all aspects except for printing, due to the different __repr__() method. Inheriting from dict to get __repr__() would require reimplementation of each and every method, as is done for collections.OrderedDict. If one wants only a readonly view, one can inherit from collections.Mapping and save the implementation of __setitem__() and __delitem__(). I find DictView useful to select parameters from self.__dict__ and pass them on in a compact form.

like image 58
Stefan Avatar answered Oct 27 '22 10:10

Stefan


This is pretty easy to implement:

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)
like image 27
Jochen Ritzel Avatar answered Oct 27 '22 10:10

Jochen Ritzel