Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define a python dictionary with immutable keys but mutable values

Well, the question is in the title: how do I define a python dictionary with immutable keys but mutable values? I came up with this (in python 2.x):

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)

but it looks to me (unsurprisingly) rather sloppy. In particular, is this safe or is there the risk of actually changing/adding some keys, since I'm inheriting from dict? Thanks.

like image 853
user2061949 Avatar asked Feb 11 '13 16:02

user2061949


2 Answers

Consider proxying dict instead of subclassing it. That means that only the methods that you define will be allowed, instead of falling back to dict's implementations.

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]

Also, you should use string formatting instead of + to generate the error message, since otherwise it will crash for any value that's not a string.

like image 143
Katriel Avatar answered Sep 21 '22 04:09

Katriel


The problem with direct inheritance from dict is that it's quite hard to comply with the full dict's contract (e.g. in your case, update method won't behave in a consistent way).

What you want, is to extend the 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

Note that the original (wrapped) dict will be modified, if you don't want that to happen, use copy or deepcopy.

like image 44
bereal Avatar answered Sep 20 '22 04:09

bereal