Abstract: Python’s ChainMap provides a single effective tool for managing multiple dictionaries from the Collections module.

This article to share from huawei cloud community “from scratch to learn python | ChainMap manage multiple context”, the author: Yuchuan.

Sometimes, when you use multiple different dictionaries, you need to group and manage them as one. In other cases, you can have multiple dictionaries representing different scopes or contexts and need to treat them as a single dictionary so that you can access the underlying data in a given order or priority. In this case, you can use Python’s ChainMap from the Collections module.

ChainMap combines multiple dictionaries and maps in an updatable view with dictionary-like behavior. In addition, ChainMap provides functionality that allows you to effectively manage various dictionaries, define key lookup priorities, and more.

In this tutorial, you will learn how to:

  • Create a ChainMap instance in a Python program

  • Explore the differences between ChainMap and dict

  • Use ChainMap to combine multiple dictionaries into one

  • ChainMap is preferred for administrative key lookup

To get the most out of this tutorial, you should know the basics of using dictionaries and lists in Python.

At the end of your journey, you’ll find some practical examples that will help you understand ChainMap better.

Introduction to Python ChainMap

Python’s ChainMap joins Python 3.3 in Collections as a convenient scope and environment for management. This class allows you to combine multiple dictionaries and other mappings so that they appear logically and as a whole. It creates a single updatable view that works like a regular dictionary, but with some internal differences.

ChainMap does not combine its maps together. Instead, it keeps them in an internal mapping list. ChainMap then reimplements the common dictionary operations at the top of the list. Because the internal list holds references to the original input map, any change in these maps affects the entire ChainMap object.

Storing input mappings in a list allows you to have duplicate keys in a given chain map. If you perform a key lookup, ChainMap searches the list of mappings until it finds the first occurrence of the target key. If the key is lost, then you get KeyError as usual.

Storing the mappings in a list really comes into play when you need to manage nested scopes, where each map represents a specific scope or context.

To better understand what scope and context mean, consider how Python resolves names. When Python looks for a name, it searches in locals(), globals(), and finally builtins until it finds the first occurrence of the target name. If the name doesn’t exist, then you get a NameError. Handling scope and context is the most common problem with ChainMap that you can solve.

When you use ChainMap, you can link multiple dictionaries using disjoint or intersecting keys.

In the first case, ChainMap allows you to treat all dictionaries as one. Therefore, you can access key-value pairs just as you would a single dictionary. In the second case, in addition to managing the dictionary as one, you can also use an internal mapping list to define some kind of access priority for repeating keys in the dictionary. This is why the ChainMap object is ideal for handling multiple contexts.

One strange behavior ChainMap is mutations, such as update, add, Delete, Clear, and eject keys, that only apply to the first map in the internal map list. Here is a summary of the main features of ChainMap:

  • Build updatable views from multiple input mappings

  • Provides almost the same interface as a dictionary, but with some additional functionality

  • Do not merge the input mappings, but save them in an internal public list

  • View external changes to the input mapping

  • You can contain duplicate keys with different values

  • Search the keys in order through the internal mapping list

  • Raises A when KeyError is missing a key after searching the entire map list

  • Only the first mapping in the internal list is changed

In this tutorial, you will learn about ChainMap in detail. The following sections will guide you through how ChainMap can create new instances in your code.

Instantiation ChainMap

To create a ChainMap in your Python code, you first need to import collections from the class and then call it as usual. The class initializer can take zero or more mappings as parameters. With no arguments, it initializes a chain map with an empty dictionary inside:

>>> from collections import ChainMap
>>> from collections import OrderedDict, defaultdict

>>> # Use no arguments
>>> ChainMap()
ChainMap({})

>>> # Use regular dictionaries
>>> numbers = {"one": 1, "two": 2}
>>> letters = {"a": "A", "b": "B"}

>>> ChainMap(numbers, letters)
ChainMap({'one': 1, 'two': 2}, {'a': 'A', 'b': 'B'})

>>> ChainMap(numbers, {"a": "A", "b": "B"})
ChainMap({'one': 1, 'two': 2}, {'a': 'A', 'b': 'B'})

>>> # Use other mappings
>>> numbers = OrderedDict(one=1, two=2)
>>> letters = defaultdict(str, {"a": "A", "b": "B"})
>>> ChainMap(numbers, letters)
ChainMap(
    OrderedDict([('one', 1), ('two', 2)]),
    defaultdict(<class 'str'>, {'a': 'A', 'b': 'B'})
)
Copy the code

Here, you ChainMap creates multiple objects using different mapping combinations. In each case, ChainMap returns a single dictionary-like view of all input mappings. Note that you can use any type of mapping, such as OrderedDict and DefaultDict.

You can also ChainMap using class methods to create an object.fromkeys (). This method can take iterable keys and optional defaults for all keys:

>>> from collections import ChainMap

>>> ChainMap.fromkeys(["one", "two","three"])
ChainMap({'one': None, 'two': None, 'three': None})

>>> ChainMap.fromkeys(["one", "two","three"], 0)
ChainMap({'one': 0, 'two': 0, 'three': 0})
Copy the code

If you call.fromkeys() on ChainMap with the iteration key as the argument, then you get a dictionary of the ChainMap. The key is from the input iterable, and the value defaults to None. Alternatively, you can pass a second argument to.fromkeys() to provide a reasonable default value for each key.

Run a dictionary-like operation

ChainMap supports the same API as regular dictionaries for accessing existing keys. Once you have a ChainMap object, you can use a dictionarie-style key lookup to retrieve existing keys, or you can use.get() :

>>> from collections import ChainMap >>> numbers = {"one": 1, "two": 2} >>> letters = {"a": "A", "b": "B"} >>> alpha_num = ChainMap(numbers, letters) >>> alpha_num["two"] 2 >>> alpha_num.get("a") 'A' >>> alpha_num["three"] Traceback (most recent call last): . KeyError: 'three'Copy the code

Key lookup searches all mappings in the target chain map until the desired key is found. If the key does not exist, then you get the usual KeyError. Now, how does the lookup operation behave when you have duplicate keys? In this case, you will get the target key for the first occurrence:

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"dogs": 4, "cats": 3, "turtles": 1}
>>> pets = ChainMap(for_adoption, vet_treatment)

>>> pets["dogs"]
10
>>> pets.get("cats")
7
>>> pets["turtles"]
1
Copy the code

When you access “cats” for repeating keys (such as “dogs”and), the chain mapping returns only the first occurrence of that key. Internally, the lookup operation searches for the input mappings in the same order that they appear in the internal mapping list, which is the exact order in which you pass them to the initializers of the class.

This general behavior also applies to iterations:

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"dogs": 4, "cats": 3, "turtles": 1}
>>> pets = ChainMap(for_adoption, vet_treatment)

>>> for key, value in pets.items():
...     print(key, "->", value)
...
dogs -> 10
cats -> 7
turtles -> 1
pythons -> 3
Copy the code

The for loop iterates over pets in the dictionary and prints the first occurrence of each key-value pair. You can also traverse the dictionary directly or using and. Keys (),.values() just like any dictionary:

>>> from collections import ChainMap >>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3} >>> vet_treatment = {"dogs": 4, "cats": 3, "turtles": 1} >>> pets = ChainMap(for_adoption, vet_treatment) >>> for key in pets: ... print(key, "->", pets[key]) ... dogs -> 10 cats -> 7 turtles -> 1 pythons -> 3 >>> for key in pets.keys(): ... print(key, "->", pets[key]) ... dogs -> 10 cats -> 7 turtles -> 1 pythons -> 3 >>> for value in pets.values(): ... print(value) ... 10 July 1 3Copy the code

Again, the behavior is the same. Each iteration goes through the first occurrence of each key, item, and value in the underlying chain map.

ChainMap also supports mutations. In other words, it allows you to update, add, delete, and eject key-value pairs. The difference in this case is that these operations only apply to the first mapping:

>>> from collections import ChainMap

>>> numbers = {"one": 1, "two": 2}
>>> letters = {"a": "A", "b": "B"}

>>> alpha_num = ChainMap(numbers, letters)
>>> alpha_num
ChainMap({'one': 1, 'two': 2}, {'a': 'A', 'b': 'B'})

>>> # Add a new key-value pair
>>> alpha_num["c"] = "C"
>>> alpha_num
ChainMap({'one': 1, 'two': 2, 'c': 'C'}, {'a': 'A', 'b': 'B'})

>>> # Update an existing key
>>> alpha_num["b"] = "b"
>>> alpha_num
ChainMap({'one': 1, 'two': 2, 'c': 'C', 'b': 'b'}, {'a': 'A', 'b': 'B'})

>>> # Pop keys
>>> alpha_num.pop("two")
2
>>> alpha_num.pop("a")
Traceback (most recent call last):
    ...
KeyError: "Key not found in the first mapping: 'a'"

>>> # Delete keys
>>> del alpha_num["c"]
>>> alpha_num
ChainMap({'one': 1, 'b': 'b'}, {'a': 'A', 'b': 'B'})
>>> del alpha_num["a"]
Traceback (most recent call last):
    ...
KeyError: "Key not found in the first mapping: 'a'"

>>> # Clear the dictionary
>>> alpha_num.clear()
>>> alpha_num
ChainMap({}, {'a': 'A', 'b': 'B'})
Copy the code

Changing the contents of a given chain map only affects the first map, even if the key you are trying to change exists in other maps in the list. For example, when you try to update “b” in the second mapping, what really happens is that you add a new key to the first dictionary.

You can use this behavior to create an updatable chain map that does not modify the original input dictionary. In this case, you can use the empty dictionary as the first argument ChainMap:

>>> from collections import ChainMap

>>> numbers = {"one": 1, "two": 2}
>>> letters = {"a": "A", "b": "B"}

>>> alpha_num = ChainMap({}, numbers, letters)
>>> alpha_num
ChainMap({}, {'one': 1, 'two': 2}, {'a': 'A', 'b': 'B'})

>>> alpha_num["comma"] = ","
>>> alpha_num["period"] = "."

>>> alpha_num
ChainMap(
    {'comma': ',', 'period': '.'},
    {'one': 1, 'two': 2},
    {'a': 'A', 'b': 'B'}
)
Copy the code

Here, you create alpha_num with an empty dictionary ({}). This ensures that the changes you make to alpha_num never affect your two original input dictionaries, Numbers and Letters, and only the empty dictionary at the beginning of the list.

Merge and link dictionaries

As an alternative to using ChainMap to link multiple dictionaries, consider combining them using dict.update() :

>>> from collections import ChainMap

>>> # Chain dictionaries with ChainMap
>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"hamsters": 2, "turtles": 1}

>>> ChainMap(for_adoption, vet_treatment)
ChainMap(
    {'dogs': 10, 'cats': 7, 'pythons': 3},
    {'hamsters': 2, 'turtles': 1}
)

>>> # Merge dictionaries with .update()
>>> pets = {}
>>> pets.update(for_adoption)
>>> pets.update(vet_treatment)
>>> pets
{'dogs': 10, 'cats': 7, 'pythons': 3, 'hamsters': 2, 'turtles': 1}
Copy the code

In this particular example, you get similar results when you build the chain map and the equivalent dictionary from two existing dictionaries that have unique keys.

The first and most important disadvantage of comparing dictionaries.update () to chainmap. is that you cannot use multiple scopes or contexts to manage and prioritize access to duplicate keys. With.update(), the last value you supplied for a given key will always prevail:

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"cats": 2, "dogs": 1}

>>> # Merge dictionaries with .update()
>>> pets = {}
>>> pets.update(for_adoption)
>>> pets.update(vet_treatment)
>>> pets
{'dogs': 1, 'cats': 2, 'pythons': 3}
Copy the code

Regular dictionaries cannot store duplicate keys. Each time the value of an existing.update() key is called, the key is updated to the new value. In this case, you will not be able to prioritize access to duplicate keys using different scopes.

** Note: ** Starting with Python 3.5, you can also combine dictionaries using the dictionary unpacking operator (**). In addition, if you are using Python 3.9, you can use the dictionary joint operator (|) to combine two dictionaries for a new dictionary.

Now suppose you have n different mappings, each of which has at most M keys. ChainMap takes O (n) execution time to create objects from them, and in the worst case, it takes O (n) to retrieve keys where the target key is in the last dictionary of the internal map list.

Alternatively,.update() requires O (nm) to create a regular dictionary in the loop, and O (1) to retrieve a key from the final dictionary.

The conclusion is that if you create dictionary chains frequently and perform only a few key lookups at a time, you should use ChainMap. If it’s the other way around, use a regular dictionary, unless you need duplicate keys or multiple ranges.

Another difference between a merged dictionary and a linked dictionary is that when you use ChainMap, external changes in the input dictionary affect the underlying chain, which is not the case with a merged dictionary.

Explore the additional ChainMap feature

ChainMap provides much of the same API and functionality as a regular Python dictionary, with some minor differences that you already know.

ChainMap also supports additional features specific to its design and goals.

In this section, you’ll learn about all of these additional features. When you access the key-value pairs in the dictionary, you’ll learn how they help you manage different scopes and contexts.

Manage the mapping list.maps

ChainMap stores all input mappings in an internal list. This list is accessible through a public instance property named.maps and can be updated by the user. Maps order matches ChainMap to the order you passed them to. This order defines the search order when the key find operation is performed.

Here’s an example of how you can do that.

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"dogs": 4, "turtles": 1}

>>> pets = ChainMap(for_adoption, vet_treatment)
>>> pets.maps
[{'dogs': 10, 'cats': 7, 'pythons': 3}, {'dogs': 4, 'turtles': 1}]
Copy the code

Here, you use.maps to access an internal list of mappings saved by PETS. This list is a regular Python list, so you can manually add and remove mappings, traverse the list, change the order of mappings, and so on:

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"cats": 1}
>>> pets = ChainMap(for_adoption, vet_treatment)

>>> pets.maps.append({"hamsters": 2})
>>> pets.maps
[{'dogs': 10, 'cats': 7, 'pythons': 3}, {"cats": 1}, {'hamsters': 2}]

>>> del pets.maps[1]
>>> pets.maps
[{'dogs': 10, 'cats': 7, 'pythons': 3}, {'hamsters': 2}]

>>> for mapping in pets.maps:
...     print(mapping)
...
{'dogs': 10, 'cats': 7, 'pythons': 3}
{'hamsters': 2}
Copy the code

In these examples, you first add a new dictionary to.append() in.mapsusing. Then use the del keyword to delete dictionary 1 at the location. You can manage.Maps just like any regular Python list.

** Note: ** Internal list of maps. Maps will always contain at least one map. For example, if you use ChainMap() to create an empty ChainMap with no arguments, the list will store an empty dictionary.

You can.maps iterate over all the maps as you perform operations on them. The possibility of traversing the list of mappings allows you to do different things for each mapping. Using this option, you can resolve the default behavior of changing only the first mapping in the list.

An interesting example is that you can reverse the order of the current mapping list by using the following command: reverse() :

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"cats": 1}
>>> pets = ChainMap(for_adoption, vet_treatment)
>>> pets
ChainMap({'dogs': 10, 'cats': 7, 'pythons': 3}, {'cats': 1})

>>> pets.maps.reverse()
>>> pets
ChainMap({'cats': 1}, {'dogs': 10, 'cats': 7, 'pythons': 3})
Copy the code

The Reverse internal map list allows you to reverse the search order when looking for a given key in a chain map. Now, when you look for “cats”, you get the number of cats that have been treated by veterinarians, not the number of cats that are being adopted.

Add a new subcontext.new_child ()

ChainMap also implements.new_child(). This method optionally takes a map as an argument and returns a new ChainMap instance containing the input map followed by all current mappings in the underlying ChainMap:

>>> from collections import ChainMap

>>> mom = {"name": "Jane", "age": 31}
>>> dad = {"name": "John", "age": 35}

>>> family = ChainMap(mom, dad)
>>> family
ChainMap({'name': 'Jane', 'age': 31}, {'name': 'John', 'age': 35})

>>> son = {"name": "Mike", "age": 0}
>>> family = family.new_child(son)

>>> for person in family.maps:
...     print(person)
...
{'name': 'Mike', 'age': 0}
{'name': 'Jane', 'age': 31}
{'name': 'John', 'age': 35}
Copy the code

In this case,.new_child() returns a ChainMap containing the new object son for the new mapping, followed by the old mapping, MOM, and dad. Notice that the new map now occupies the first place in the internal map list,.maps.

Using.new_child(), you create a subcontext that you can update without changing any of the existing mappings. For example, if you call.new_child() with no arguments, it uses an empty dictionary and places it in.maps. After that, you can make any changes to the new empty map so that the rest of the map remains read-only.

Skip the subcontext. Parents

Another interesting feature of ChainMap is.parents. This property returns a new ChainMap instance that contains all the mappings in the underlying ChainMap except the first one. This feature is useful for skipping the first map when you are searching for keys in a given chain map:

>>> from collections import ChainMap

>>> mom = {"name": "Jane", "age": 31}
>>> dad = {"name": "John", "age": 35}
>>> son = {"name": "Mike", "age":  0}

>>> family = ChainMap(son, mom, dad)
>>> family
ChainMap(
    {'name': 'Mike', 'age': 0},
    {'name': 'Jane', 'age': 31},
    {'name': 'John', 'age': 35}
)

>>> family.parents
ChainMap({'name': 'Jane', 'age': 31}, {'name': 'John', 'age': 35})
Copy the code

In this example, you use.parents to skip the first dictionary that contains the son data. In a way,.parents is the inverse of.new_child(). The former removes a dictionary, while the latter adds a new dictionary at the beginning of the list. In both cases, you get a new chain diagram.

Manage scope and context ChainMap

Arguably, the main use case, ChainMap, is to provide an efficient way to manage multiple scopes or contexts and handle access priorities for duplicate keys. This is useful when you have multiple dictionaries that store duplicate keys and you want to define the order in which code accesses them.

In the ChainMap documentation, you’ll find a classic example that simulates how Python resolves variable names in different namespaces.

When Python looks for a name, it searches local, global, and built-in scopes in the same order until it finds the target name. A Python scope is a dictionary that maps names to objects.

To simulate Python’s internal lookup chain, you can use a chain mapping:

>>> import builtins

>>> # Shadow input with a global name
>>> input = 42

>>> pylookup = ChainMap(locals(), globals(), vars(builtins))

>>> # Retrieve input from the global namespace
>>> pylookup["input"]
42

>>> # Remove input from the global namespace
>>> del globals()["input"]

>>> # Retrieve input from the builtins namespace
>>> pylookup["input"]
<built-in function input>
Copy the code

In this example, you first create a global variable named input, which hides the built-in function in the input() function builtins field. You then create pyLookup a chain map containing three dictionaries that hold each Python scope.

When you input from retrieve when pylookup, you get 42 values from the global scope. If you input removes the key from the globals() dictionary and accesses it again, then your input() will get the built-in function from the builtins scope, which has the lowest priority in Python’s lookup chain.

Also, you can use ChainMap to define and manage the key lookup order for duplicate keys. This allows you to preferentially access duplicate key instances as needed.

ChainMap is tracked in the standard library

The origins of ChainMap are closely related to the performance issues of ConfigParser, which lives in the ConfigParser standard library module. With ChainMap, core Python developers optimize configParser.get ().

You can also find ChainMap as part of the module. This class takes a string template as an argument and allows you to perform the string replacement described in PEP 292. The input string template contains the embedded identifier that you can later replace with the actual value: Templatestring

>>> import string >>> greeting = "Hey $name, welcome to $place!" >>> template = string.Template(greeting) >>> template.substitute({"name": "Jane", "place": "the World"}) 'Hey Jane, welcome to the World! 'Copy the code

When you supply the value name to the dictionary and place supplies the value through the dictionary,.substitute() replaces them in the template string. In addition,.substitute() can take values as keyword arguments (**kwargs), which can cause name conflicts in some cases:

>>> import string >>> greeting = "Hey $name, welcome to $place!" >>> template = string.Template(greeting) >>> template.substitute( ... {"name": "Jane", "place": "the World"}, ... place="Real Python" ... ) 'Hey Jane, welcome to Real Python! 'Copy the code

In this example,.substitute() replaces place with the value you provide as a keyword argument, rather than the value in the input dictionary. If you dig into the code for this method, you’ll see that it is used for ChainMap to effectively manage the priority of input values in case of name conflicts.

Substitute () :

# string.py
# Snip...
from collections import ChainMap as _ChainMap

_sentinel_dict = {}

class Template:
    """A string class for supporting $-substitutions."""
    # Snip...

    def substitute(self, mapping=_sentinel_dict, /, **kws):
        if mapping is _sentinel_dict:
            mapping = kws
        elif kws:
            mapping = _ChainMap(kws, mapping)
        # Snip...
Copy the code

This is where the highlighted lines come into play. It uses a chain mapping that takes two dictionaries, KWS and Mapping, as parameters. This method sets the priority of repeated identifiers in the input data by placing KWS as the first parameter.

Put PythonChainMap into action

So far, you’ve learned how to combine ChainMap dictionaries into one. You also learned about the features and differences between ChainMap and a regular dictionary. The use case for ChainMap is quite specific. They include:

  • Effectively group multiple dictionaries in a single view

  • Search for multiple dictionaries with a specific priority

  • Provide a set of default values and manage their priority

  • Improves the performance of code that frequently computes subsets of dictionaries

In this section, you’ll write some real-world examples that will help you better understand how to use ChainMap to solve real-world problems.

Access multiple inventories as one

The first example of code you’ll write is for ChainMap to efficiently search multiple dictionaries in a single view. In this case, you assume that you have a bunch of independent dictionaries with unique keys.

Suppose you are running a store that sells fruits and vegetables. You’ve written a Python application to manage your inventory. The application reads data from the database and returns two dictionaries, one for fruit and one for vegetable prices. You need an efficient way to group and manage this data in a single dictionary.

After some research, you end up using ChainMap:

>>> From collections import ChainMap >>> fruits_prices = {"apple": 0.80, "grape": 0.40, "orange": 0.50} > > > veggies_prices = {" tomato ": 1.20," pepper ": 1.30," onion ": >>> prices = ChainMap(fruits_prices, veggies_prices) >>> order = {"apple": 4, "tomato": 8, "orange": 4} >>> for product, units in order.items(): ... price = prices[product] ... subtotal = units * price ... Print (f "{product: 6} : ${price: 2 f} x = ${units} {subtotal: 2 f}")... Apple: $0.80 × 4 = $3.20 Tomato: $1.20 × 8 = $9.60 Orange: $0.50 × 4 = $2.00Copy the code

In this example, you use aChainMap to create a dictionary-like object that groups the data from fruits_prices and veggies_prices. This allows you to access the underlying data as if you effectively had a single dictionary. The for loop iterates over the products in a given order. It then calculates the subtotals for each product type and prints them on your screen.

You might want to consider grouping data into a new dictionary, with.update() used in the loop. This may work well when you have a limited range of products and a small inventory. However, if you manage many different types of products, then.update() with ChainMap. Using ChainMap to solve this problem can also help you define the priority of different batches of products, allowing you to manage a first-in/first-out (FIFO) approach to your inventory.

Prioritize command-line application Settings

ChainMap is particularly useful for managing default configuration values in your application. As you know, one of its main features, ChainMap, allows you to set the priority of key lookup operations. This sounds like the right tool to solve the problem of managing application configuration.

For example, suppose you are developing a command line interface (CLI) application. This application allows the user to specify the proxy service used to connect to the Internet. Set the priority to:

1. Command-line options (–proxy, -p)

2. Local configuration file in the user home directory

3. Configure agents in the system

If the user provides an agent on the command line, the application must use the agent. Otherwise, the application should use the proxy provided in the next configuration object, and so on. This is one of the most common use cases, ChainMap. In this case, you can do the following:

>>> from collections import ChainMap

>>> cmd_proxy = {}  # The user doesn't provide a proxy
>>> local_proxy = {"proxy": "proxy.local.com"}
>>> system_proxy = {"proxy": "proxy.global.com"}

>>> config = ChainMap(cmd_proxy, local_proxy, system_proxy)
>>> config["proxy"]
'proxy.local.com'
Copy the code

ChainMap allows you to define the appropriate priorities for your application’s proxy configuration. Key search searches cmd_proxy, then local_proxy, and finally system_proxy, returning the first instance of the key at hand. In the example, the user does not provide the agent on the command line, so the application gets the agent local_proxy from, which is the next setting provider in the list.

Manages default parameter values

Another use case for ChainMap is to manage default parameter values in methods and functions. Suppose you’re writing an application to manage data about your company’s employees. You have the following class, which represents a generic user:

class User:
    def __init__(self, name, user_id, role):
        self.name = name
        self.user_id = user_id
        self.role = role

    # Snip...
Copy the code

At some point, you’ll need to add a feature that allows employees to access different components of the CRM system. Your first thought is to modify User to add new functionality. However, this might make the class too complex, so you decide to create a subclass, CRMUser, to provide the required functionality.

This class takes the user name and CRMcomponent as parameters. It also needs some **kwargs. You want to implement CRMUser in a way that allows you to provide reasonable defaults for base class initializers without losing **kwargs. Here’s how you can solve the problem ChainMap using the following method:

from collections import ChainMap

class CRMUser(User):
    def __init__(self, name, component, **kwargs):
        defaults = {"user_id": next(component.user_id), "role": "read"}
        super().__init__(name, **ChainMap(kwargs, defaults))
Copy the code

In this code example, you will create the User. In class initialization, you take name, Component, and **kwargs as parameters. Then, you create a local dictionary where user_id and role have a default value. __init__() then uses super(). In this call, you pass your name directly to the parent’s initializer and use the chain map to provide default values for the rest of the parameters.

Note that the ChainMap object takes kwargs and then defaults as parameters. This order ensures that the parameters (kwargs) that are supplied manually when the class is instantiated take precedence over the defaults values.

conclusion

Python’s ChainMap is provided from the Collections module for managing multiple dictionaries as a single effective tool. This class is handy when you have multiple dictionaries representing different scopes or contexts and need to prioritize access to the underlying data.

ChainMap combines multiple dictionaries and maps in an updatable view that works much like a dictionary. You can use the ChainMap object to efficiently handle multiple dictionaries, define key lookup priorities, and manage multiple contexts in Python.

In this tutorial, you learned how to:

  • Create a ChainMap instance in a Python program

  • Explore the differences between ChainMap and dict

  • Use multiple dictionaries as one management ChainMap

  • Set the priority ChainMap for key lookup operations

In this tutorial, you’ve also written some practical examples to help you better understand when and how ChainMap can be used in Python code.

Click follow to learn about the fresh technologies of Huawei Cloud