4

I have a dictionary filled with key-object pairs. I want to make the dictionary immutable and I thought the best/easiest way is to cast it to a frozenset but frozenset(dict) and also tuple(dict) only stores the keys.

Using frozenset(dict.items()) I seem to get a frozenset with the key-object pairs but I don't know how to retrieve the values/keys.

I have the following code which works, as long as "__obfuscators" is a dictionary

def obfuscate_value(self, key, value):
    obfuscator = self.__obfuscators.get(key)
    if obfuscator is not None:
        return obfuscator.obfuscate_value(value)
    else:
        return value

I tried this in an attempt to get it working with the frozen set:

def obfuscate_value(self, key, value):
    try:
        obfuscator = self.__obfuscators[key]
    except:
        return value
    return obfuscator.obfuscate_value(value)

but this gives that frozenset does not have \__getitem__ and self.__obfuscators.__getattribute__(key) always says it does not have the attribute (because I assume this searches for a function named key) Is there a better way to make the dictionary immutable or how can I retrieve the object depending on the key?

Edit: I ended up casting the dict to a tuple using tuple(obfuscator.items()) and then wrote my own find value function:

def find_obfuscator(self, key):
    for item in self.__obfuscators:
        x, y = item
        if self.case_insensitive:
            if x.lower() == key.lower():
                return y
        else:
            if x == key:
                return y

I would like to thank everyone for their efforts and input.

2
  • what about subclassingcollections.Mapping or using a Mappingproxy. As far as I know both are immutable (for a given value of immutable). Commented Aug 4, 2016 at 14:39
  • Does this answer your question? What would a "frozen dict" be? Commented Feb 3, 2021 at 23:17

5 Answers 5

3

You can create an immutable view of a dictionary using types.MappingProxyType:

from types import MappingProxyType
d = { 'a': 1 }
fd = MappingProxyType(d)
fd['a']
#output:
1

fd['a'] = 2
#output:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'mappingproxy' object does not support item assignment

notice that you can still change the vaule object, so:

d = { 'a': [1] }
fd = MappingProxyType(d)
fd['a'].append(2)
fd['a']
#output:
[1,2]

will work.

Sign up to request clarification or add additional context in comments.

Comments

1

The simplest way I could think of to achieve what you want was to subclass the standard dict type and overwrite its __setitem__ method:

class MyDict(dict):
    def __setitem__(self, key, value):
        raise NotImplementedError("This is a frozen dictionary")

This allows you to create dictionaries that cannot thereafter be changed by item assignment:

d = MyDict({1: 2, 3: 4})

or, equivalently:

d = MyDict([(1, 2), (3, 4)])

The dict then prints out just like a standard dict:

{1: 2, 3: 4}

But when you try to change a value (or add a new one):

d[1] = 15
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-21-a22420992053> in <module>()
----> 1 d[1] = 34

<ipython-input-18-03f266502231> in __setitem__(self, key, value)
      1 class MyDict(dict):
      2     def __setitem__(self, key, value):
----> 3         raise NotImplementedError("This is a frozen dictionary")

NotImplementedError: This is a frozen dictionary

Note that this isn't fully immutable, however:

d.update({1:17})

for example, will update it, but this solution might be good enough - it depends on the broader requirements.

Comments

0

You could make a wrapper class that takes a dictionary and has a get item function but no set item. You'd need to add a few things for thread safety and hashing maybe but the basic class wouldn't be too difficult.

1 Comment

I did think about this but I hoped there would be an easier/faster solution than making an own custom dictionary class. Especially since I seemed like I was so close now because I have a frozenset with key-object pairs, but I just cannot access them or at least I don't know how
0

Since you mention tuple(dict) in your original post, probably the simplest solution to achieve what you want might simply be:

tuple(dict.items())

2 Comments

with this option I still face the same problem as in the frozenset(dict.items()). How can I access the value, based on the key?
when thinking about this option again I realized it would probably be the easiers solution, if I made my own search function since all the other options are not more simple
0

You need a dict that is capable of freezing? You can simply make one:

class FrozenDict(dict):
    def __init__(self, *args, **kwargs):
        self._frozen = False
        dict.__init__(self, *args, **kwargs)
    def freeze(self):
        self._frozen = True
    def __setitem__(self, key, value):
        if (self._frozen):
            raise TypeError("Attempted assignment to a frozen dict")
        else:
            return dict.__setitem__(self, key, value)

a = FrozenDict({7:8})
a[5] = 6
print(a)
a.freeze()
a[3] = 2 # raises TypeError

It will behave exactly like usual dict until you call .freeze(). Then it's frozen.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.