A ChainMap is a data structure in the collections module that allows for multiple mappings( mostly dictionaries) to be linked together and treated like a single unit. 

In a single ChainMap multiple dictionaries are connected in a chain-like manner. This allows for efficient lookup of values, since each dictionary is searched in turn, going from the first to the last. When a key is found in a dictionary, the corresponding value is returned and the search is terminated. If the key is not found in any of the dictionaries, an exception is raised. 

The ChainMap keeps references to the original dictionaries, this means that  if any of the underlying dictionaries are modified, those changes will be reflected in the ChainMap.

Instantiating ChainMaps

To create a ChainMap, we start by importing the class from the collections module then use the following syntax.

ChainMap(*maps)

Where the parameter maps represents the arbitrary dict objects(or other mappings).

Create a ChainMap

#import the ChainMap class
from collections import ChainMap

d1 = {'one': 1, 'two': 2, 'three': 3}
d2 = {'four': 4, 'five': 5, 'six': 6}
d3 = {'seven': 7, 'eight': 8, 'nine': 9}

chain = ChainMap(d1, d2, d3)

print(chain)

Any modifications done to any of the linked dictionaries will be reflected in the ChainMap.

#import the ChainMap class
from collections import ChainMap

d1 = {'one': 1, 'two': 2, 'three': 3}
d2 = {'four': 4, 'five': 5, 'six': 6}
d3 = {'seven': 7, 'eight': 8, 'nine': 9}

chain = ChainMap(d1, d2, d3)

d1['zero'] = 0

print(chain)

Retrieving  data

Access the value associated with a key

When we access an element from the ChainMap the element is looked up in the first map in the chain, if it is not found, then the lookup is continued in the next map in the chain (if any). This process continues until the element is found in one of the maps. If the element is not found in any of the maps, then just like in dictionaries, a KeyError is raised.

#import the ChainMap class
from collections import ChainMap

d1 = {'one': 1, 'two': 2, 'three': 3}
d2 = {'four': 4, 'five': 5, 'six': 6}
d3 = {'seven': 7, 'eight': 8, 'nine': 9}

chain = ChainMap(d1, d2, d3)

print(chain['three'])
print(chain['five'])
print(chain['eight'])
print(chain['nine'])

Just like in dictionaries we can use the get() method to access an element and specify a default value that will be returned in case the key does not exist in any of the linked dictionaries.

#import the ChainMap class
from collections import ChainMap

d1 = {'one': 1, 'two': 2, 'three': 3}
d2 = {'four': 4, 'five': 5, 'six': 6}
d3 = {'seven': 7, 'eight': 8, 'nine': 9}

chain = ChainMap(d1, d2, d3)

print(chain.get('two'))
print(chain.get('six'))
print(chain.get('seven'))
print(chain.get('ten', 'not found'))

ChainMap information

The ChainMap class defines the items()keys(), and  values() methods which returns  the items, the keys and the values in the ChainMap, respectively.

from collections import ChainMap

Europe = {'Germany': 'Berlin', 'France': 'Paris', 'Spain': 'Madrid'}
Africa = {'Nairobi': 'Kenya', 'Rwanda': 'Kigali', 'Nigeria': 'Abuja'}
Asia = {'Japan': 'Tokyo', 'China': 'Beijing', 'India': 'Delhi'}

chain = ChainMap(Europe, Africa, Asia)

print("items:")
print(chain.items(), '\n')

print("keys:")
print(chain.keys(), '\n')

print("values:")
print(chain.values(), '\n')

Inserting and Modifying data 

The new_child() method adds a new dictionary at the beginning of the chain and returns a new ChainMap reflecting the change.

from collections import ChainMap

Europe = {'Germany': 'Berlin', 'France': 'Paris', 'Spain': 'Madrid'}
Africa = {'Nairobi': 'Kenya', 'Rwanda': 'Kigali', 'Nigeria': 'Abuja'}
Asia = {'Japan': 'Tokyo', 'China': 'Beijing', 'India': 'Delhi'}


chain = ChainMap(Europe, Africa, Asia)

Americas = {'Canada': 'Ottawa', 'USA': 'Washington', 'Peru': 'Lima' }

updated_chain = chain.new_child(Americas)

print(updated_chain)

You can also insert single elements using the chain[key] = value syntax.