I have a defaultdict(list)
which I'd like to make immutable so that I can add objects of this type to a set. I have an idea as to how I can make it immutable, but it would require me write a few lines of code. Is there not a simpler way of doing this in python?
Isn't it common in python to populate, for example a defaultdict(list)
, while parsing a piece of data before freezing it once the parsing is complete?
Its also the case that an object can be of type tuple but can't be used as a dict key, for example:
>>> a = ([1,2],[2,3])
>>> type(a)
<type 'tuple'>
>>> d = dict()
>>> d[a] = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Why there exists a tuple of lists in python is also something I dont understand.
Another solution is to use pyrsistent package link. Pyrsistent is a number of persistent collections (by some referred to as functional data structures). Persistent in the sense that they are immutable.
I recommend using freeze link.
A minimal working example (MWE):
from pyrsistent import freeze
d = {"a":1, "b": 2}
d = freeze(d) # immutable dictionary
print(d)
## pmap({'b': 2, 'a': 1})
# Try to change a key-value
d["b"] = 10
## TypeError: 'PMap' object does not support item assignment
# If you want to change a value you need to create a new object using "update"
d.update({"b":10})
## pmap({'b': 10, 'a': 1})
To make a mutable object immutable, all its mutable containers must be replaced by their immutable counterparts. A dictionary where all values are immutable themselves can be made immutable trivially.
Adapting the example from the defaultdict(list)
documentation:
import collections as coll
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = coll.defaultdict(tuple)
for k, v in s:
d[k] = d[k] + (v,)
print(d)
# prints
defaultdict(<class 'tuple'>, {'yellow': (1, 3), 'blue': (2, 4), 'red': (1,)})
The keys
in our defaultdict(tuple)
are immutable (strings
), and values as well (tuple
), as opposed to a defaultdict(list)
.
To freeze this dictionary:
def dict_freeze(d):
# This is the trivial one-line function
# It assumes the values are of immutable types, i.e. no lists.
# It unpacks dict items, sorts and converts to tuple
# sorting isn't strictly necessary, but dictionaries don't preserve order
# thus we could end up with the following:
# d = {'a': 1, 'b': 2} could get frozen as either of the following
# (('a', 1), ('b', 2)) != (('b', 2), ('a', 1))
return tuple(sorted(d.items()))
frozen_d = dict_freeze(d)
print(frozen_d)
# prints
(('blue', (2, 4)), ('red', (1,)), ('yellow', (1, 3)))
Thus, I would recommend using defaultdict(tuple) instead of defaultdict(list) for this case and to freeze, just unpack, sort and convert to a tuple.
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