Blog

MappingProxyType from types module

Note: This blogpost is available as a Jupyter notebook on GitHub.

The types module [https://github.com/topper-123/Articles/blob/master/New-interesting-data-types-in-Python3.rst] in Python allows us to create custom dynamic types. In this blogpost, we will learn about MappingProxyType class from the types module by creating a proxy dictionary. Why would we need a proxy dictionary? The MappingProxyType creates a read only copy of dictionary that protects the original dictionary from any updates. In a large application, it is possible to accidentally modify the content of a mutable data structure like a dictionary. By converting a dictionary to read-only, we ensure that its value cannot be tampered. The original dictionary can be mutated but the read only MappingProxyType cannot be mutated.

In the below code, we create a dictionary called D1 with two key-value pairs. We supply D1 to MappingProxyType to create a new dictionary that is a proxy to D1.

import types
D1 = {'a' : 100, 'b' : 110}
# D1 is a dictionary that is created in Python 3.5,
#for this reason it is not an ordered dictionary
D2 = types.MappingProxyType(D1) # D2 is a read only proxy of D1
print(D1, D2)

{'a': 100, 'b': 110} {'a': 100, 'b': 110}


Since D1 is a dictionary, we can update D1 by assigning a new key-value pair to it.


D1['c'] = 120 # updating D1

When we print D2 notice that D2 is updated as it points to D1 via the proxy.


print(D2) # D2 was updated when we added a new key-value pair to D1

{'a': 100, 'c': 120, 'b': 110}


When we try to update D2 by assigning a new key-value pair, the TypeError exception is raised because D2 is a read only proxy of D1.


D2['d'] = 130 # can't add a new key-value pair to D2

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

<ipython-input-7-e1ea60b8061d> in <module>()
----> 1 D2['d'] = 130 # can't add a new key-value pair to D2

TypeError: 'mappingproxy' object does not support item assignment


When we try to update an existing key in D2, again TypeError exception is raised because D2 is a read only proxy of D1.


D2['c'] = 102 # can't assign a new value to existing key in D2

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

<ipython-input-8-fd217326f187> in <module>()
----> 1 D2['c'] = 102 # can't assign a new value to existing key in D2

TypeError: 'mappingproxy' object does not support item assignment


In the next few examples, we will demonstrate that a MappingProxyType and dictionary are similar by calling methods that we normally use with dictionaries. For example, we can access the key and values from D2 by using the items() method just like in a regular dictionary. Below, we loop through the D2.items() and obtain each key and value and then print them.


# we can loop through key-values in D2
for k, v in D2.items():
    print(k, v)

a 100
c 120
b 110


We are checking whether key 'a' is in D2 by using the 'in' membership operator. Since 'a' is a key in D2, 'a' in D2 will return True.


# checking membership
print('a' in D2)

True


We can use the length function to determine the number of key-values in D2.


print(len(D2)) # returns the number of key-values in D2

3


We create a shallow copy of D2 and call it D3. Notice that the id of D3 and id of D2 are same.


D3 = D2
print(id(D3), id(D2))

2921153002760 2921153002760


Since D3 is a shallow copy of D2, which is a proxy of D1. When we add a new key-value to D1, D2 and D3 also get updated as shown in the output.


D1['e'] = 160
print("D1 - ", D1)
print("D2 - ", D2)
print("D3 - ", D3)

D1 - {'a': 100, 'c': 120, 'e': 160, 'b': 110}
D2 - {'a': 100, 'c': 120, 'e': 160, 'b': 110}
D3 - {'a': 100, 'c': 120, 'e': 160, 'b': 110}


We can create a copy of D2 by using the copy() method and call it D4. This copy can be mutated while the original cannot be mutated.


D4 = D2.copy() # D3 is a copy of D2

Any change made to D4, will not affect D1 or D2. Below we are updating the value for key 'c' in D4 and notice that neither D1 nor D2 will be updated.


D4['c'] = 102
print("D1 - ", D1)
print("D2 - ", D2)
print("D4 - ", D4)

D1 - {'a': 100, 'c': 120, 'e': 160, 'b': 110}
D2 - {'a': 100, 'c': 120, 'e': 160, 'b': 110}
D4 - {'a': 100, 'c': 102, 'b': 110, 'e': 160}


Since D4 is an independent copy and is not related to D1 or D2, any change to D1 will not be reflected in D4. Adding a new key-value to D1. We are printing D1, D2 and D4. Notice that D1 and D2 are updated but not D4.


D1['d'] = 150
print("D1 - ", D1)
print("D2 - ", D2)
print("D4 - ", D4)

D1 - {'a': 100, 'c': 120, 'e': 160, 'b': 110, 'd': 150}
D2 - {'a': 100, 'c': 120, 'e': 160, 'b': 110, 'd': 150}
D4 - {'a': 100, 'c': 102, 'b': 110, 'e': 160}


We are printing the ids of all the four dictionaries. Even though D2 points to D1, D2 is an object by itself so it has a different id. Since D3 is a shallow copy of D2, its id is same as D2's. Since D4 is a deep copy of D2, its id is different.


print(id(D1), id(D2), id(D3), id(D4))

2921153832968 2921153002760 2921153002760 2921153833416

Even though we demonstrated use of MappingProxyType with a dictionary, it can also be used with other mappings such as defaultdict and OrderedDict.  

To conclude, in this blogpost we saw an example on when to use MappingProxyType from the types module.

Comments