1 # adapted from jaraco.collections 3.9 |
|
2 |
|
3 import collections |
|
4 |
|
5 |
|
6 class Projection(collections.abc.Mapping): |
|
7 """ |
|
8 Project a set of keys over a mapping |
|
9 |
|
10 >>> sample = {'a': 1, 'b': 2, 'c': 3} |
|
11 >>> prj = Projection(['a', 'c', 'd'], sample) |
|
12 >>> prj == {'a': 1, 'c': 3} |
|
13 True |
|
14 |
|
15 Keys should only appear if they were specified and exist in the space. |
|
16 |
|
17 >>> sorted(list(prj.keys())) |
|
18 ['a', 'c'] |
|
19 |
|
20 Attempting to access a key not in the projection |
|
21 results in a KeyError. |
|
22 |
|
23 >>> prj['b'] |
|
24 Traceback (most recent call last): |
|
25 ... |
|
26 KeyError: 'b' |
|
27 |
|
28 Use the projection to update another dict. |
|
29 |
|
30 >>> target = {'a': 2, 'b': 2} |
|
31 >>> target.update(prj) |
|
32 >>> target == {'a': 1, 'b': 2, 'c': 3} |
|
33 True |
|
34 |
|
35 Also note that Projection keeps a reference to the original dict, so |
|
36 if you modify the original dict, that could modify the Projection. |
|
37 |
|
38 >>> del sample['a'] |
|
39 >>> dict(prj) |
|
40 {'c': 3} |
|
41 """ |
|
42 |
|
43 def __init__(self, keys, space): |
|
44 self._keys = tuple(keys) |
|
45 self._space = space |
|
46 |
|
47 def __getitem__(self, key): |
|
48 if key not in self._keys: |
|
49 raise KeyError(key) |
|
50 return self._space[key] |
|
51 |
|
52 def __iter__(self): |
|
53 return iter(set(self._keys).intersection(self._space)) |
|
54 |
|
55 def __len__(self): |
|
56 return len(tuple(iter(self))) |
|