Python: Return Dictionary as ReadOnly from Class

Updated: Jun 11, 2018


Personally I have had many situations where I had to return a dictionary from a class to the caller and I did not initially think on encapsulation violation but is such easy thing to overlook. Lets look at an example:


class ConfigParser:

def __init__(self):

self._config = {}


def addEntry(self, name, value):

self._config[name] = value


def getValue(self, name):

return self._config.get(name, None)


def getConfig(self):

return self._config


config = ConfigParser()

config.addEntry( "timeout", 30 )

config.addEntry( "retry", 5 )


print("timeout=", config.getValue("timeout"))


config.getConfig()["timeout"] = 50

print("Retrieving again, timeout=", config.getValue("timeout"))


Result:

timeout= 30

Retrieving again, timeout= 50


Now some purist will argue we don’t really need to return back dict from our config parser class, but for the sake of example, lets keep that aside for a moment. There might be multiple such situations when you return dict object from you class and caller now has access to this dict object without any restrictions. That clearly is encapsulation violation, which python is okay with but then obviously you lose advantages of encapsulation. One of the important fact in OOPS based classes is that more you expose your internal datastructures of your class to callers, more harder your class would be for changes. And it is generally bad assumption to assume that class will not change.


So what is wrong with above example? One important problem is we are returning read-write instance of dict object to the caller or user of our class. Again, ideal would be to remove such interface for class and add specific key lookup interfaces, but again, for the sake of example lets assume caller needs such instance to serialize and dump to a file (or sends it over network to other program running on another host) so we do need it. So how can we fix this? One another way to do this would be to return read-only dict object from our class so caller can’t really update any fields. So how do we do that? Python comes to rescue with types. MappingProxyType. Below is updated example re-written with it:


from types import MappingProxyType


class ConfigParser:

def __init__(self):

self._config = {}


def addEntry(self, name, value):

self._config[name] = value


def getValue(self, name):

return self._config.get(name, None)


def getConfig(self):

return MappingProxyType(self._config)


config = ConfigParser()

config.addEntry( "timeout", 30 )

config.addEntry( "retry", 5 )

print("timeout=", config.getValue("timeout"))


try:

print("Trying to update dictionary which is read-only ..")

config.getConfig()["timeout"] = 50

except TypeError as e:

print("We received an exception: %s" % e)


Result:

timeout= 30

Trying to update dictionary which is read-only ..

We received an exception: 'mappingproxy' object does not support item assignment


This is nice! Now we return a read-only instance of our dictionary object which caller can use like any other dictionary for reads but writes are prevented! This small technique comes so handy so you should know it.