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
| 994 | ######################################################################## |
| 995 | |
| 996 | class ChainMap(_collections_abc.MutableMapping): |
| 997 | ''' A ChainMap groups multiple dicts (or other mappings) together |
| 998 | to create a single, updateable view. |
| 999 | |
| 1000 | The underlying mappings are stored in a list. That list is public and can |
| 1001 | be accessed or updated using the *maps* attribute. There is no other |
| 1002 | state. |
| 1003 | |
| 1004 | Lookups search the underlying mappings successively until a key is found. |
| 1005 | In contrast, writes, updates, and deletions only operate on the first |
| 1006 | mapping. |
| 1007 | |
| 1008 | ''' |
| 1009 | |
| 1010 | def __init__(self, *maps): |
| 1011 | '''Initialize a ChainMap by setting *maps* to the given mappings. |
| 1012 | If no mappings are provided, a single empty dictionary is used. |
| 1013 | |
| 1014 | ''' |
| 1015 | self.maps = list(maps) or [{}] # always at least one map |
| 1016 | |
| 1017 | def __missing__(self, key): |
| 1018 | raise KeyError(key) |
| 1019 | |
| 1020 | def __getitem__(self, key): |
| 1021 | for mapping in self.maps: |
| 1022 | try: |
| 1023 | return mapping[key] # can't use 'key in mapping' with defaultdict |
| 1024 | except KeyError: |
| 1025 | pass |
| 1026 | return self.__missing__(key) # support subclasses that define __missing__ |
| 1027 | |
| 1028 | def get(self, key, default=None): |
| 1029 | return self[key] if key in self else default # needs to make use of __contains__ |
| 1030 | |
| 1031 | def __len__(self): |
| 1032 | return len(set().union(*self.maps)) # reuses stored hash values if possible |
| 1033 | |
| 1034 | def __iter__(self): |
| 1035 | d = {} |
| 1036 | for mapping in map(dict.fromkeys, reversed(self.maps)): |
| 1037 | d |= mapping # reuses stored hash values if possible |
| 1038 | return iter(d) |
| 1039 | |
| 1040 | def __contains__(self, key): |
| 1041 | for mapping in self.maps: |
| 1042 | if key in mapping: |
| 1043 | return True |
| 1044 | return False |
| 1045 | |
| 1046 | def __bool__(self): |
| 1047 | return any(self.maps) |
| 1048 | |
| 1049 | @_recursive_repr() |
| 1050 | def __repr__(self): |
| 1051 | return f'{self.__class__.__name__}({", ".join(map(repr, self.maps))})' |
| 1052 | |
| 1053 | @classmethod |
no outgoing calls