ChainMap – Collections in Python

ChainMap is one of the most commonly used class provided by the collections module. collections module in Python provides specialized container data types that extend the capabilities of built-in data structures like lists, dictionaries, and tuples. These container data types are designed to make it easier to manage and manipulate data in specific and more efficient ways.

ChainMap

In Python, ChainMap class is part of the collections module and allows us to group multiple dictionaries or mappings into a single view. ChainMap in short is called as collection of dictionaries. It is particularly useful when we want to search through multiple dictionaries as if they were a single dictionary.

Syntax to create ChainMap object:

chainMap_object = collections.ChainMap(dict_1, dict_2, .... , dict_N). This can also be represented as => collections.ChainMap(*dictionaries)

  • collections is a module in Python. ChainMap class is part of collections module. We need to import collections module to use ChainMap class in our program.
  • ChainMap() is the ChainMap class constructor
  • *dictionaries is the group of dictionaries to be as an argument. We can multiple dictionaries as the same time.
  • chainMap_object is the object returned

ChainMap Properties

There are many properties of Chainmap collection.

  • Order of dictionaries in ChainMap object is very important.
  • Search OrderChainMap searches for keys in the dictionaries in the order they were provided. The first mapping in the ChainMap has the highest priority. If a key is found in the first dictionary, its value is returned immediately, and the search stops. If not, the search continues through the subsequent dictionaries.
  • Dynamic ViewChainMap provides a dynamic view of the dictionaries. If any of the original dictionaries are updated or changed, those changes will be reflected in the ChainMap object.
  • Modifications – Assigning a value to a key in a ChainMap will update the first dictionary in which the key is found. If the key does not exist in any of the dictionaries, it will be added to the first dictionary in the chain.
  • Adding New Mappings – New dictionary to the front of the ChainMap can be added using the new_child() method. This creates a new ChainMap with the new dictionary as the first element, leaving the original ChainMap unchanged.
  • Accessing Dictionariesmaps attribute of a ChainMap returns a list of the dictionaries it contains. We can access, modify, or reorder these dictionaries if needed.
  • Key Deletion – Deleting a key from a ChainMap will only remove the key from the first dictionary in which it is found. If the key is not found in the first dictionary, a KeyError is raised.
  • Read-only ViewChainMap provides a unified view of multiple dictionaries, it does not merge the dictionaries. Each dictionary remains distinct, and no data is copied. This makes ChainMap memory-efficient, especially when dealing with large datasets or numerous dictionaries.
  • ChainMap is useful when we have a series of dictionaries where earlier ones can override later ones. This is often the case with layered configuration systems (e.g., command-line arguments, environment variables, and default settings).
  • Iteration – Iterating over a ChainMap yields keys from the first dictionary that contains them, preserving the order of the dictionaries. Duplicate keys in later dictionaries are ignored during iteration.
  • CompatibilityChainMap works with any mapping type, not just dictionaries. We can include any object that implements the mapping interface (supports key-value pairs) in a ChainMap.

ChainMap Object

To create ChainMap object, we need to import it from the collections module. We can create an empty ChainMap object OR a ChainMap object by passing in two or more dictionaries. Order of dictionaries is very important. The dictionaries are searched in the order they are passed.

Empty ChainMap Object

from collections import ChainMap          # Import ChainMap from collections module

# create empty ChainMap
print("create empty ChainMap")
empty_chain_map = ChainMap()            # Use ChainMap() constructor
print(empty_chain_map)
print(type(empty_chain_map))                # Print type of ChainMap object

In this program, we have imported ChainMap from collections module and we are using ChainMap() constructor, without any dictionary to create an empty ChainMap object.

Program Output

create empty ChainMap
ChainMap({})
<class 'collections.ChainMap'>

Empty ChainMap({}) is created and type of this object is <class 'collections.ChainMap'>.

ChainMap Object with Dictionaries

We can group multiple dictionaries or other mappings (key-value pairs) together to create a single, unified view of Chainmap object.
# Chainmap object with dictionaries
print("Chainmap object with dictionaries")

from collections import ChainMap          # Import Chainmap from collections module

training_dict = {"1": "Shbytes", "2": "Online", "3": "Training"}
courses_dict = {"c1": "Python", "c2": "AWS", "c3": "Azure"}
dicts_chain_map = ChainMap(training_dict, courses_dict)  # Use Chainmap() constructor with dictionaries

print(dicts_chain_map)
print(type(dicts_chain_map))

In this program, we have imported ChainMap from collections module and we are using ChainMap() constructor, with two dictionaries to create a ChainMap object.

Program Output

Chainmap object with dictionaries
ChainMap({'1': 'Shbytes', '2': 'Online', '3': 'Training'}, {'c1': 'Python', 'c2': 'AWS', 'c3': 'Azure'})
<class 'collections.ChainMap'>

ChainMap with two dictionary chain is created and type of this object is <class 'collections.ChainMap'>.

new_child() method – add child at front of ChainMap

We can add a new dictionary or mapping (key-value pair) to the front of the ChainMap using the new_child() method.

  • Use new_child() method to add an empty dictionary or to add a dictionary with elements.
  • A new ChainMap object is created, with the new dictionary as the first element.
  • new_child() method does not make any change in original ChainMap object.

Add empty child at front of ChainMap

# new_child() - add new empty child map at front of ChainMap
print("new_child() - add new empty child map at front of ChainMap")

from collections import ChainMap

training_dict = {"1": "shbytes", "2": "Online", "3": "Training"}
courses_dict = {"c1": "Python", "c2": "AWS", "c3": "Azure"}
shbytes_chain_map = ChainMap(training_dict, courses_dict)
print(shbytes_chain_map)

new_shbytes_chain_map = shbytes_chain_map.new_child()   # Added new child at front of ChainMap object
print(new_shbytes_chain_map)

In this program, we have imported ChainMap class from collections module. Then we create a ChainMap object shbytes_chain_map with two dictionaries. We are using shbytes_chain_map.new_child() to add a new child at the front of the ChainMap object. This will create a new ChainMap object new_shbytes_chain_map without making change change to original object shbytes_chain_map.

Program Output

new_child() - add new empty child map at front of ChainMap
# original ChainMap object
ChainMap({'1': 'shbytes', '2': 'Online', '3': 'Training'}, {'c1': 'Python', 'c2': 'AWS', 'c3': 'Azure'})
# new ChainMap object with empty dictionary as its first element
ChainMap({}, {'1': 'shbytes', '2': 'Online', '3': 'Training'}, {'c1': 'Python', 'c2': 'AWS', 'c3': 'Azure'})

From the output of this program, we have new ChainMap object with empty dictionary as its first element.

Add dictionary with elements as new child at front of ChainMap

#new_child(map) - add dictionary as new child map at front of ChainMap
print("new_child(map) - add dictionary as new child map at front of ChainMap")

from collections import ChainMap

training_dict = {"1":"shbytes","2":"Online","3":"Training"}
courses_dict = {"c1":"Python","c2":"AWS","c3":"Azure"}
shbytes_chainmap = ChainMap(training_dict, courses_dict)   # create a ChainMap object
print(shbytes_chainmap)

# Added dictionary with elements as new child at front of ChainMap object
level_shbytes_chainmap = shbytes_chainmap.new_child({"l1":"intermediate","l2":"expert"})
print(level_shbytes_chainmap)

In this program, we have imported ChainMap class from collections module. Then we create a ChainMap object shbytes_chainmap with two dictionaries. We are using shbytes_chainmap.new_child({"l1":"intermediate","l2":"expert"}) to add a dictionary as new child at the front of the ChainMap object. This will create a new ChainMap object level_shbytes_chainmap without making change change to original object shbytes_chainmap.

Program Output

new_child(map) - add dictionary as new child map at front of ChainMap
# original ChainMap object
ChainMap({'1': 'shbytes', '2': 'Online', '3': 'Training'}, {'c1': 'Python', 'c2': 'AWS', 'c3': 'Azure'})
# new ChainMap object with dictionary elements as its first element
ChainMap({'l1': 'intermediate', 'l2': 'expert'}, {'1': 'shbytes', '2': 'Online', '3': 'Training'}, {'c1': 'Python', 'c2': 'AWS', 'c3': 'Azure'})

From the output of this program, new ChainMap object has dictionary {'l1': 'intermediate', 'l2': 'expert'} added as its first element.

maps attribute – access ChainMap Mappings

To access the ChainMap mappings, we can use maps attribute of a ChainMap. maps attribute returns a list of the dictionaries in the ChainMap object. This can be used to access, modify, or reorder these dictionaries.

from collections import ChainMap

# maps - returns list of maps in ChainMap
print("maps - returns list of maps in ChainMap")

training_dict = {"1": "shbytes", "2": "Online", "3": "Training"}
courses_dict = {"c1": "Python", "c2": "AWS", "c3": "Azure"}
shbytes_chain_map = ChainMap(training_dict, courses_dict)

print(shbytes_chain_map)
print(shbytes_chain_map.maps)     # Access mappings using maps attribute

Program Output

maps - returns list of maps in ChainMap
ChainMap({'1': 'shbytes', '2': 'Online', '3': 'Training'}, {'c1': 'Python', 'c2': 'AWS', 'c3': 'Azure'})
[{'1': 'shbytes', '2': 'Online', '3': 'Training'}, {'c1': 'Python', 'c2': 'AWS', 'c3': 'Azure'}]

maps attribute returns a list of dictionaries or mappings (key-value pairs) from the given ChainMap object.

parents attribute – all ChainMap mappings except first

We can access all dictionaries or mappings (key-value pair) except first, from ChainMap object using the parents attribute.

  • Use parents attribute to get all dictionaries or mappings (key-value pair) except first.
  • A new ChainMap object is created, which will not have first dictionary.
  • parents attribute does not make any change in original ChainMap object.
#return new ChainMap containing all of the maps in current instance except the first one
print("return new ChainMap containing all of the maps in current instance except the first one")

from collections import ChainMap

dict_1 = {1: 'a', 2: 'b'}
dict_2 = {3: 'c', 4: 'd'}
dict_3 = {5: 'e', 6: 'f'}
shbytes_chainmap = ChainMap(dict_1, dict_2, dict_3) 

parent_shbytes_chainmap = shbytes_chainmap.parents    # Access all dictionaries except first one
print(parent_shbytes_chainmap)

In this program, We are using shbytes_chainmap.parents to get all dictionaries except first one, from the ChainMap object. This will create a new ChainMap object parent_shbytes_chainmap without making change change to original object shbytes_chainmap.

Program Output

return new ChainMap containing all of the maps in current instance except the first one
# Original ChainMap object containing three dictionaries
ChainMap({1: 'a', 2: 'b'}, {3: 'c', 4: 'd'}, {5: 'e', 6: 'f'})

# ChainMap object with parents (all dictionaries except first)
ChainMap({3: 'c', 4: 'd'}, {5: 'e', 6: 'f'})

From the program output, new ChainMap object has two dictionaries ChainMap({3: 'c', 4: 'd'}, {5: 'e', 6: 'f'}) except first one.

keys(), values(), items() method in ChainMap

ChainMap class provides methods to access keys, values and items of all dictionaries or mappings (key-value pairs) from the ChainMap object

  • chain.keys() – Gives us the KeysView of all the unique keys from the ChainMap object. KeysView from this can be converted into any collection datatype like list, set, tuple etc. like list(chain.keys())
  • chain.values() – Gives us the ValuesView of all the unique values from the ChainMap object. ValuesView from this can be converted into any collection datatype like list, set, tuple etc. like list(chain.values())
  • chain.items() – Gives us the ItemsView of all the unique items from the ChainMap object. ItemsView from this can be converted into any collection datatype like list, set, tuple etc. like list(chain.items())
#get all keys, values, items from ChainMap
print("get all keys, values, items from ChainMap")

from collections import ChainMap

dict_1 = {1: 'a', 2: 'b', 3: 'c'}
dict_2 = {3: 'c', 4: 'd', 5: 'e'}
dict_3 = {5: 'e', 6: 'f', 7: 'g'}
shbytes_chainmap = ChainMap(dict_1, dict_2, dict_3)    # created ChainMap object of three dictionaries
print(shbytes_chainmap)

all_keys = shbytes_chainmap.keys()                  # Get KeysView from the ChainMap object
print("all keys view in ChainMap - ", all_keys)
print("all keys as list - ", list(all_keys))        # Get all unique keys as list from KeysView
print("all keys as set - ", set(all_keys))          # Get all unique keys as set from KeysView

all_values = shbytes_chainmap.values()              # Get ValuesView from the ChainMap object
print("all values view in ChainMap - ", all_values)
print("all values as list - ", list(all_values))    # Get all unique values as list from ValuesView
print("all values as set - ", set(all_values))      # Get all unique values as set from ValuesView

all_items = shbytes_chainmap.items()                # Get ItemsView from the ChainMap object
print("all items view in ChainMap - ", all_items)
print("all items as list - ", list(all_items))      # Get all unique items as list from ItemsView
print("all items as set - ", set(all_items))        # Get all unique items as set from ItemsView

Program Output for chain.keys(), chain.values(), chain.items()

get all keys, values, items from ChainMap
ChainMap({1: 'a', 2: 'b', 3: 'c'}, {3: 'c', 4: 'd', 5: 'e'}, {5: 'e', 6: 'f', 7: 'g'})

all keys view in ChainMap -  KeysView(ChainMap({1: 'a', 2: 'b', 3: 'c'}, {3: 'c', 4: 'd', 5: 'e'}, {5: 'e', 6: 'f', 7: 'g'}))
all keys as list -  [5, 6, 7, 3, 4, 1, 2]
all keys as set -  {1, 2, 3, 4, 5, 6, 7}

all values view in ChainMap -  ValuesView(ChainMap({1: 'a', 2: 'b', 3: 'c'}, {3: 'c', 4: 'd', 5: 'e'}, {5: 'e', 6: 'f', 7: 'g'}))
all values as list -  ['e', 'f', 'g', 'c', 'd', 'a', 'b']
all values as set -  {'g', 'a', 'e', 'b', 'c', 'd', 'f'}

all items view in ChainMap -  ItemsView(ChainMap({1: 'a', 2: 'b', 3: 'c'}, {3: 'c', 4: 'd', 5: 'e'}, {5: 'e', 6: 'f', 7: 'g'}))
all items as list -  [(5, 'e'), (6, 'f'), (7, 'g'), (3, 'c'), (4, 'd'), (1, 'a'), (2, 'b')]
all items as set -  {(1, 'a'), (3, 'c'), (4, 'd'), (2, 'b'), (7, 'g'), (5, 'e'), (6, 'f')}

Collection from KeysView, ValuesView and ItemsView gives the unique keys, values and items respectively from all the dictionaries or mappings of the ChainMap object. List or Set collection for keys, values or items does not contain any duplicate element.

Get value at key – ChainMap

  • We can use chain.get(key) or chain[key] to get the value at given key.
  • ChainMap searches for keys in the dictionaries in the order they were provided. The first mapping in the ChainMap has the highest priority. If a key is found in the first dictionary, its value is returned immediately, and the search stops. If not, the search continues through the subsequent dictionaries.
  • If key does not found in any of the dictionaries of ChainMap then None is returned.
#get value at given key in ChainMap
print("get value at given key in ChainMap")

from collections import ChainMap

dict_1 = {1: 'a', 2: 'b', 3: 'c'}
dict_2 = {3: 'd', 4: 'e', 5: 'dict_2_5'}
dict_3 = {5: 'dict_3_5', 6: 'h', 7: 'i'}
shbytes_chainmap = ChainMap(dict_1, dict_2, dict_3)
print(shbytes_chainmap)

print("value at key 4 - ", shbytes_chainmap.get(4))     # get value using chain.get(key) method
print("value at key 5 - ", shbytes_chainmap[5])            # get value using chain[key]
print("value at key 12 - ", shbytes_chainmap.get(12))    # get value for key not present in ChainMap

In this program, we have create a ChainMap object shbytes_chainmap with three dictionaries.

  • dict_1 and dict_2 has common key 3 with different values.
  • dict_2 and dict_3 has common key 5 with different values.
  • shbytes_chainmap.get(4) will return the value at key 4 from dict_2
  • shbytes_chainmap[5] will return the value at key 5 from dict_2. Based on the order of dictionaries dict_2 is of higher order than dict_3. Key 5 from dict_3 will not be iterated.
  • shbytes_chainmap.get(12) will return None. Key 12 does not exists in any of dictionaries in ChainMap object.

Program Output

get value at given key in ChainMap
ChainMap({1: 'a', 2: 'b', 3: 'c'}, {3: 'd', 4: 'e', 5: 'dict_2_5'}, {5: 'dict_3_5', 6: 'h', 7: 'i'})
value at key 4 -  e
value at key 5 -  dict_2_5
value at key 12 -  None

Create ChainMap with default values

We can use chain.fromkeys(iterable, value) to create ChainMap object with default value.

print("fromkeys returns dictionary with the specified keys and value")
from collections import ChainMap

new_shbytes_chainmap = ChainMap.fromkeys([3, 'c2','l1','l2'], 'expert')
print(new_shbytes_chainmap)

In this program, we are using ChainMap.fromkeys([3, 'c2','l1','l2'], 'expert') to create ChainMap object with multiple elements each having default value.

Program Output

fromkeys returns dictionary with the specified keys and value
ChainMap({3: 'expert', 'c2': 'expert', 'l1': 'expert', 'l2': 'expert'})

This creates a ChainMap object with dictionary. Dictionary will have elements key from the iterable and with default value.

update() method

  • Syntax – chain.update({key:value,....,key:value})
  • update() method looks for the elements only in the first dictionary of ChainMap
  • If key found in the first dictionary, then it will update the value of that key
  • else, it will add new key-value pair in the first dictionary
print("update() - inserts or updates the specified items in first dictionary")

from collections import ChainMap

dict_1 = {1: 'a', 2: 'b', 3: 'c'}
dict_2 = {3: 'd', 4: 'e', 5: 'dict_2_5'}
dict_3 = {5: 'dict_3_5', 6: 'h', 7: 'i'}
shbytes_chainmap = ChainMap(dict_1, dict_2, dict_3)
print(shbytes_chainmap)

shbytes_chainmap.update({3:"Learning"})    # Update key 3 in first dictionary
shbytes_chainmap.update({4:"Training"})    # Will add key 4 in the first dictionary
print(shbytes_chainmap)

In this program, we have created a ChainMap object ChainMap(dict_1, dict_2, dict_3). In this ChainMap object dict_1 is the first dictionary.

  • Using shbytes_chainmap.update({3:"Learning"}) will update the value of key 3 in first dictionary, because key 3 already present in dict_1.
  • Using shbytes_chainmap.update({4:"Training"}) will add new key-value pair in first dictionary, because key 4 does not exists in dict_1.

Program Output

update() - inserts or updates the specified items in first dictionary
ChainMap({1: 'a', 2: 'b', 3: 'c'}, {3: 'd', 4: 'e', 5: 'dict_2_5'}, {5: 'dict_3_5', 6: 'h', 7: 'i'})
ChainMap({1: 'a', 2: 'b', 3: 'Learning', 4: 'Training'}, {3: 'd', 4: 'e', 5: 'dict_2_5'}, {5: 'dict_3_5', 6: 'h', 7: 'i'})

copy() method

chain.copy() creates a deep copy of the ChainMap object. After creating a copy, changes in the original ChainMap object will affect the new object.

#copy() - returns deep copy
print("copy() - returns deep copy")
from collections import ChainMap

training_dict = {1:"shbytes",2:"Online",3:"Training"}
courses_dict = {"c1":"Python","c2":"PowerBI","c3":"AWS"}
shbytes_chainmap = ChainMap(training_dict, courses_dict) 
print(shbytes_chainmap)

copy_shbytes_chainmap = shbytes_chainmap.copy()     # ChainMap copy
print(copy_shbytes_chainmap)

shbytes_chainmap["c3"] = "LLM"    # Adding element in original object
print(copy_shbytes_chainmap)
  • We have create a ChainMap object with two dictionaries, using ChainMap(training_dict, courses_dict)
  • shbytes_chainmap.copy() will create a deep copy of the of the object
  • shbytes_chainmap["c3"] = "LLM", we are adding new element in original object which should not be reflected in copied object.

Program Output

copy() - returns deep copy
ChainMap({1: 'shbytes', 2: 'Online', 3: 'Training'}, {'c1': 'Python', 'c2': 'PowerBI', 'c3': 'AWS'})
ChainMap({1: 'shbytes', 2: 'Online', 3: 'Training'}, {'c1': 'Python', 'c2': 'PowerBI', 'c3': 'AWS'})
ChainMap({1: 'shbytes', 2: 'Online', 3: 'Training'}, {'c1': 'Python', 'c2': 'PowerBI', 'c3': 'AWS'})
# Same elements in copied object, even when original object was updated

Remove elements from ChainMap

  • chain.pop(key) removes the given key from the first dictionary of ChainMap object. It returns the value of the key in dictionary. KeyError is raised if key is not present in first dictionary of ChainMap object.
  • chain.popitem() will remove the last element from the first dictionary. KeyError is raised if first dictionary is empty.
  • del chain[key] can be used to delete the key from the first dictionary of ChainMap object. KeyError is raised if key is not present in first dictionary of ChainMap object.
# remove elements from first dictionary of ChainMap
print("remove elements from first dictionary of ChainMap")
from collections import ChainMap

courses_dict = {"c1":"Python","c2":"AWS","c3":"Azure", "c4":"LLM"}
training_dict = {1:"shbytes",2:"Online",3:"Training"}
shbytes_chainmap = ChainMap(courses_dict, training_dict)
print(shbytes_chainmap)

pop_value = shbytes_chainmap.pop("c2")     # Take out key c2 from chainmap
print(shbytes_chainmap)                    # Print chainmap elements after removing key c2
print(pop_value)                           # Print return value for key c2 

print("popitem - take out last item with given key from first dictioary in ChainMap")
popitem_element = shbytes_chainmap.popitem()    # Takeout last item from the first dictionary
print(shbytes_chainmap)                         # Print chainmap elements after removing last item
print(popitem_element)                          # Print return item

del shbytes_chainmap["c3"]    # Delete key c3 from first dictionary
print(shbytes_chainmap)       # Print chainmap after deleting c3 key

Program Output

remove elements from first dictionary of ChainMap
# original ChainMap object
ChainMap({'c1': 'Python', 'c2': 'AWS', 'c3': 'Azure', 'c4': 'LLM'}, {1: 'shbytes', 2: 'Online', 3: 'Training'})
# Elements after pop key c2
ChainMap({'c1': 'Python', 'c3': 'Azure', 'c4': 'LLM'}, {1: 'shbytes', 2: 'Online', 3: 'Training'})
AWS     # Return value of key c2

popitem - take out last item with given key from first dictioary in ChainMap
# Elements after popitem last element
ChainMap({'c1': 'Python', 'c3': 'Azure'}, {1: 'shbytes', 2: 'Online', 3: 'Training'})
('c4', 'LLM')     # Return item

# Elements after delete key c3
ChainMap({'c1': 'Python'}, {1: 'shbytes', 2: 'Online', 3: 'Training'})

Summary

In this article, we learned about ChainMap collection in Python. Following topics were discussed:

Code – Github Repository

All code snippets and programs for this article and for Python tutorial, can be accessed from Github repository – Comments and Docstring in Python.

Python Topics


Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *