ThunderM
ThunderM

Reputation: 41

Immutable Dictionary with key-object pairs Python

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.

Upvotes: 4

Views: 5120

Answers (5)

Daerdemandt
Daerdemandt

Reputation: 2381

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.

Upvotes: 0

Ohad Eytan
Ohad Eytan

Reputation: 8464

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.

Upvotes: 3

norok2
norok2

Reputation: 26956

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

tuple(dict.items())

Upvotes: 0

holdenweb
holdenweb

Reputation: 37203

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.

Upvotes: 1

Dullvampire
Dullvampire

Reputation: 26

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.

Upvotes: 0

Related Questions