| import collections |
| import itertools |
| |
| |
| # from jaraco.collections 3.5.1 |
| class DictStack(list, collections.abc.Mapping): |
| """ |
| A stack of dictionaries that behaves as a view on those dictionaries, |
| giving preference to the last. |
| |
| >>> stack = DictStack([dict(a=1, c=2), dict(b=2, a=2)]) |
| >>> stack['a'] |
| 2 |
| >>> stack['b'] |
| 2 |
| >>> stack['c'] |
| 2 |
| >>> len(stack) |
| 3 |
| >>> stack.push(dict(a=3)) |
| >>> stack['a'] |
| 3 |
| >>> set(stack.keys()) == set(['a', 'b', 'c']) |
| True |
| >>> set(stack.items()) == set([('a', 3), ('b', 2), ('c', 2)]) |
| True |
| >>> dict(**stack) == dict(stack) == dict(a=3, c=2, b=2) |
| True |
| >>> d = stack.pop() |
| >>> stack['a'] |
| 2 |
| >>> d = stack.pop() |
| >>> stack['a'] |
| 1 |
| >>> stack.get('b', None) |
| >>> 'c' in stack |
| True |
| """ |
| |
| def __iter__(self): |
| dicts = list.__iter__(self) |
| return iter(set(itertools.chain.from_iterable(c.keys() for c in dicts))) |
| |
| def __getitem__(self, key): |
| for scope in reversed(tuple(list.__iter__(self))): |
| if key in scope: |
| return scope[key] |
| raise KeyError(key) |
| |
| push = list.append |
| |
| def __contains__(self, other): |
| return collections.abc.Mapping.__contains__(self, other) |
| |
| def __len__(self): |
| return len(list(iter(self))) |