|
import collections |
|
import itertools |
|
|
|
|
|
|
|
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))) |
|
|