A ChainMap groups multiple dicts (or other mappings) together to create a single, updateable view. The underlying mappings are stored in a list. That list is public and can be accessed or updated using the *maps* attribute. There is no other state. Lookups search the underly
| 1035 | ######################################################################## |
| 1036 | |
| 1037 | class ChainMap(_collections_abc.MutableMapping): |
| 1038 | ''' A ChainMap groups multiple dicts (or other mappings) together |
| 1039 | to create a single, updateable view. |
| 1040 | |
| 1041 | The underlying mappings are stored in a list. That list is public and can |
| 1042 | be accessed or updated using the *maps* attribute. There is no other |
| 1043 | state. |
| 1044 | |
| 1045 | Lookups search the underlying mappings successively until a key is found. |
| 1046 | In contrast, writes, updates, and deletions only operate on the first |
| 1047 | mapping. |
| 1048 | |
| 1049 | ''' |
| 1050 | |
| 1051 | def __init__(self, *maps): |
| 1052 | '''Initialize a ChainMap by setting *maps* to the given mappings. |
| 1053 | If no mappings are provided, a single empty dictionary is used. |
| 1054 | |
| 1055 | ''' |
| 1056 | self.maps = list(maps) or [{}] # always at least one map |
| 1057 | |
| 1058 | def __missing__(self, key): |
| 1059 | raise KeyError(key) |
| 1060 | |
| 1061 | def __getitem__(self, key): |
| 1062 | for mapping in self.maps: |
| 1063 | try: |
| 1064 | return mapping[key] # can't use 'key in mapping' with defaultdict |
| 1065 | except KeyError: |
| 1066 | pass |
| 1067 | return self.__missing__(key) # support subclasses that define __missing__ |
| 1068 | |
| 1069 | def get(self, key, default=None): |
| 1070 | return self[key] if key in self else default # needs to make use of __contains__ |
| 1071 | |
| 1072 | def __len__(self): |
| 1073 | return len(set().union(*self.maps)) # reuses stored hash values if possible |
| 1074 | |
| 1075 | def __iter__(self): |
| 1076 | d = {} |
| 1077 | for mapping in map(dict.fromkeys, reversed(self.maps)): |
| 1078 | d |= mapping # reuses stored hash values if possible |
| 1079 | return iter(d) |
| 1080 | |
| 1081 | def __contains__(self, key): |
| 1082 | for mapping in self.maps: |
| 1083 | if key in mapping: |
| 1084 | return True |
| 1085 | return False |
| 1086 | |
| 1087 | def __bool__(self): |
| 1088 | return any(self.maps) |
| 1089 | |
| 1090 | @_recursive_repr() |
| 1091 | def __repr__(self): |
| 1092 | return f'{self.__class__.__name__}({", ".join(map(repr, self.maps))})' |
| 1093 | |
| 1094 | @classmethod |
no outgoing calls
searching dependent graphs…