Is it possible to "freeze" a python dict after creation so that it's impossible to add new keys to it? It would only be possible to change existing key values.
If not, how do you know when you are changing an existing keyvalue pair, and when you are adding a new one?
The straight answer is NO. You can not have duplicate keys in a dictionary in Python.
I would just override the __setitem__() method of your dict. Note however, that this doesn't guarantee that the values of your dict will be immutable (say you have values that are lists, for example).
frozendict is an immutable wrapper around dictionaries that implements the complete mapping interface. It can be used as a drop-in replacement for dictionaries where immutability is desired. Of course, this is python , and you can still poke around the object's internals if you want.
The frozenset type is immutable and hashable — its contents cannot be altered after it is created; it can therefore be used as a dictionary key or as an element of another set.
Maybe something like this:
class FreezableDict (dict):
__frozen = False
def freeze (self):
self.__frozen = True
def __setitem__ (self, key, value):
if self.__frozen and key not in self:
raise ValueError('Dictionary is frozen')
super().__setitem__(key, value)
>>> x = FreezableDict({'foo': 'bar', 'baz': 'bla'})
>>> x
{'baz': 'bla', 'foo': 'bar'}
>>> x['asdf'] = 'fdsa'
>>> x
{'asdf': 'fdsa', 'baz': 'bla', 'foo': 'bar'}
>>> x.freeze()
>>> x['hello'] = 'world'
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
x['hello'] = 'world'
File "<pyshell#13>", line 8, in __setitem__
raise ValueError('Dictionary is frozen')
ValueError: Dictionary is frozen
Note that you might want to overwrite other methods too, including __delitem__
, update
, setdefault
, pop
, and popitem
, as they can all modify the dictionary.
If you are interested in locking the dictionary completely, you could use types.MappingProxyType
which provides a read-only view onto your dictionary. Once you have created your normal dictionary, you can then just create a mapping proxy of it which simply does not have any of the assignment/update functionality. You can also then get rid of any reference to the original dictionary (the mapping will keep one), to prevent it from being used to update it any further:
>>> x = {'foo': 'bar'}
>>> y = types.MappingProxyType(x)
>>> y
mappingproxy({'foo': 'bar'})
>>> x['baz'] = 'bla'
>>> y
mappingproxy({'baz': 'bla', 'foo': 'bar'})
>>> y['hello'] = 'world'
Traceback (most recent call last):
File "<pyshell#55>", line 1, in <module>
y['hello'] = 'world'
TypeError: 'mappingproxy' object does not support item assignment
>>> del x
>>> y
mappingproxy({'baz': 'bla', 'foo': 'bar'})
Or just in a single line, without ever having a reference to the original dictionary:
>>> x = types.MappingProxyType({'foo': 'bar', 'baz': 'bla'})
>>> x
mappingproxy({'baz': 'bla', 'foo': 'bar'})
>>> x['hello'] = 'world'
Traceback (most recent call last):
File "<pyshell#60>", line 1, in <module>
x['hello'] = 'world'
TypeError: 'mappingproxy' object does not support item assignment
This isn't possible with a "vanilla" dict. You'll probably want to subclass collections.MutableMapping
. . .
Untested code follows
class FrozenKeyDict(collections.MutableMapping):
"""Mapping which doesn't allow keys to be added/deleted.
It does allow existing key/value pairs to be modified.
"""
def __init__(self, *args, **kwargs):
self._frozen = False
self._dict = {}
super(FrozenKeyDict, self).__init__(*args, **kwargs)
self._frozen = True
def __getitem__(self, key):
return self._dict[key]
def __setitem__(self, key, value):
if self._frozen and key not in self._dict:
raise KeyError('must be one of %s' % list(self))
self._dict[key] = value
def __delitem__(self, key):
# modify to suit your needs ...
raise KeyError('Removing keys not supported')
def __iter__(self):
return iter(self._dict)
def __len__(self):
return len(self._dict)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With