Pseudo-Immutable Dictionaries in Python
very so often a situation arises where you need to keeps some small bits of data around where you know the value, and want to ensure that it won't get fudged with in the lifespan of the application. Say, for example, you want to map some Classes to some known values for quick lookup to dynamically generate some output - [Like a dynamic django form](/2012/05/05/dynamic-django-modelforms/) In other words, you want [ENUMS](http://en.wikipedia.org/wiki/Enumerated_type).
Python doesn't have an implementation for ENUMS, and most of the standard data types are mutable, which doesn't sit well in this situation. Usually, the python swiss army knife - The Dictionary is a one size fits all solution. But, being a mutable type, don't quite fit the bill here. And, personally, using string base constants always feels hack-ish and brittle. Tuples, on the other hand are immutable. However, they are simple collections that don't allow for complex mapping. Now we could create a custom class and set up property accessors and all that fun stuff. However, there is much easier solution built-in to Python does the job in a pinch - NamedTuples
from collections import namedtuple
# A Dummy Class A
class CustomClassA( object ):
def __init__( self ):
self.A = True
# A Dummy Class B
class CustomClassB( object ):
def __init__( self ):
self.B = True
# A Named Tuple Definition
EnumType = namedtuple("EnumType", ["id", "cls"])
# 2 Instances of our new Enum type
TYPE_A = EnumType(1, CustomClassA)
TYPE_B = EnumType(2, CustomClassB)
# Use it like a dictionary
TYPE_A.cls()
>>> <class CustomClassA @0x21f67a0>
For most use cases of something that behaves like an Enum, this is probably enough. However, if we need go a little more over the edge, we can take this a little bit further by combining two data structures. One little know fact about our mutable and almost perfect friend, the dictionary - any hash-able ( read immutable ) object can be a key. More importantly, when using objects as keys, that key can only be recovered with the original object, not a copy or recreation. So we could do this:
from collections import namedtuple
# A Dummy Class A
class CustomClassA( object ):
def __init__( self ):
self.A = True
# A Dummy Class B
class CustomClassB( object ):
def __init__( self ):
self.B = True
# A Named Tuple Definition
EnumType = namedtuple("EnumType", ["id", "name"])
# 2 Instances of our new Enum type
FOO = EnumType(1, "foo")
BAR = EnumType(2, "bar")
# Use In a dictionary
MAP = {
FOO:CustomClassA,
BAR:CustomClassB
}
In this situation, unless you were to import the FOO and BAR EnumTypes to de-reference the Class objects, you would be unable to get anything out of the MAP dict.
from mymodule import FOO, BAR, MAP, EnumType
# Almost untouchable?
MAP[ EnumType(1, "foo") ] # KeyError
# use our Constant to ket the data
MAP[FOO] # <class CustomClassA>
While not a perfect solution, using named tuples as dictionaries, or combining the to for situations a bit off the wall - we can easily create custom ENUM - like constant data structures!