Welcome, guest | Sign In | My Account | Store | Cart

Encapsulates lookups into a series of namespaces.

Python, 40 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import UserDict

class Chainmap(UserDict.DictMixin):
    """Combine multiple mappings for sequential lookup.

    For example, to emulate Python's normal lookup sequence:

        import __builtin__
        pylookup = Chainmap(locals(), globals(), vars(__builtin__))        
    """

    def __init__(self, *maps):
        self._maps = maps

    def __getitem__(self, key):
        for mapping in self._maps:
            try:
                return mapping[key]
            except KeyError:
                pass
        raise KeyError(key)

if __name__ == "__main__":
    d1 = {'a':1, 'b':2}
    d2 = {'a':3, 'd':4}
    cm = Chainmap(d1, d2)
    assert cm['a'] == 1
    assert cm['b'] == 2
    assert cm['d'] == 4
    try:
        print cm['f']
    except KeyError:
        pass
    else:
        raise Exception('Did not raise KeyError for missing key')
    assert 'a' in cm  and  'b' in cm  and  'd' in cm
    assert cm.get('a', 10) == 1
    assert cm.get('b', 20) == 2
    assert cm.get('d', 30) == 4
    assert cm.get('f', 40) == 40

The underlying mapping need only define __getitem__ and raise KeyError when a key is not found.

This is a general purpose routine applicable in many contexts. Something similar was used to implement string.Template in Py2.4.

2 comments

Miki Tebeka 12 years, 12 months ago  # | flag

Why not create a new dictionary which is the "update" of all?

def chainmap(*maps):
    m = {}
    for mapping in reversed(maps):
        m.update(mapping)
        return m
Raymond Hettinger (author) 12 years, 12 months ago  # | flag

[Miki]

Why not create a new dictionary which is the "update" of all?

The update() approach works great in some situations. It is an O(n) operation that copies all of the dictionaries. In contrast, chainmap() is an O(1) step that effectively creates a view of the component mappings. Besides being fast, a view confers other advantages such as the combined mapping always reflecting any updates to the component mappings.

The chainmap() approach in used in the standard library's implementation of string.Template(). It was also used to dramatically speed-up the configparser module.