ActiveState Code

Recipe 305268: Chained map lookups


Encapsulates lookups into a series of namespaces.

Python
 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

Discussion

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.

Sign in to comment