diff options
author | Jesusaves <cpntb1@ymail.com> | 2020-12-09 13:32:01 -0300 |
---|---|---|
committer | Jesusaves <cpntb1@ymail.com> | 2020-12-09 13:32:01 -0300 |
commit | 63afe4145f410a844c647d4e3f1059f568175c1e (patch) | |
tree | 15da6a890c78d73370f44f9fd5d59badfbbe60e4 /game/python-extra/utils/dicts | |
download | client-init.tar.gz client-init.tar.bz2 client-init.tar.xz client-init.zip |
Initial commit, forked from Spheresinit
Diffstat (limited to 'game/python-extra/utils/dicts')
-rw-r--r-- | game/python-extra/utils/dicts/__init__.py | 7 | ||||
-rw-r--r-- | game/python-extra/utils/dicts/chained_dict.py | 71 | ||||
-rw-r--r-- | game/python-extra/utils/dicts/helpers.py | 99 | ||||
-rw-r--r-- | game/python-extra/utils/dicts/limited_dict.py | 40 |
4 files changed, 217 insertions, 0 deletions
diff --git a/game/python-extra/utils/dicts/__init__.py b/game/python-extra/utils/dicts/__init__.py new file mode 100644 index 0000000..0ab7c69 --- /dev/null +++ b/game/python-extra/utils/dicts/__init__.py @@ -0,0 +1,7 @@ +"""Helper functinos for dealing with dicts. + +Things you always wished you could do more succinctly! +""" +from .limited_dict import LimitedDict +from .chained_dict import ChainedDict +from .helpers import * diff --git a/game/python-extra/utils/dicts/chained_dict.py b/game/python-extra/utils/dicts/chained_dict.py new file mode 100644 index 0000000..f1fe36f --- /dev/null +++ b/game/python-extra/utils/dicts/chained_dict.py @@ -0,0 +1,71 @@ +from collections import MutableMapping +from itertools import chain + + +class ChainedDict(MutableMapping): + + def __init__(self, parent=None, **kwargs): + self.__parent = parent + self.__deleted_keys = set() + self.__data = kwargs + + def __contains__(self, key): + if self.__parent is not None: + return ( + (key in self.__data or key in self.__parent) + and key not in self.__deleted_keys + ) + return key in self.__data + + def __getitem__(self, key): + try: + return self.__data[key] + except KeyError: + if self.__parent is not None and key not in self.__deleted_keys: + return self.__parent[key] + else: + raise + + def __setitem__(self, key, val): + self.__data[key] = val + self.__deleted_keys.discard(key) + + def __delitem__(self, key): + if key in self: + self.__deleted_keys.add(key) + try: + del self.__data[key] + except KeyError: + pass + else: + raise KeyError(key) + + def __repr__(self): + return "{}({})".format(self.__class__.__name__, dict(self.items())) + + def __iter__(self): + return self.keys() + + def __len__(self): + return len(list(self.keys())) + + def iterkeys(self): + yielded = set(self.__deleted_keys) + if self.__parent is None: + iterable = self.__data.keys() + else: + iterable = chain(self.__parent.keys(), self.__data.keys()) + + for key in iterable: + if key in yielded: + continue + yield key + yielded.add(key) + + keys = iterkeys + + def iteritems(self): + for key in self.iterkeys(): + yield key, self[key] + + items = iteritems diff --git a/game/python-extra/utils/dicts/helpers.py b/game/python-extra/utils/dicts/helpers.py new file mode 100644 index 0000000..8b1f594 --- /dev/null +++ b/game/python-extra/utils/dicts/helpers.py @@ -0,0 +1,99 @@ +from collections import namedtuple + + +def from_keyed_iterable(iterable, key, filter_func=None): + """Construct a dictionary out of an iterable, using an attribute name as + the key. Optionally provide a filter function, to determine what should be + kept in the dictionary.""" + + generated = {} + + for element in iterable: + try: + k = getattr(element, key) + except AttributeError: + raise RuntimeError("{} does not have the keyed attribute: {}".format( + element, key + )) + + if filter_func is None or filter_func(element): + if k in generated: + generated[k] += [element] + else: + generated[k] = [element] + + return generated + + +def subtract_by_key(dict_a, dict_b): + """given two dicts, a and b, this function returns c = a - b, where + a - b is defined as the key difference between a and b. + + e.g., + {1:None, 2:3, 3:"yellow", 4:True} - {2:4, 1:"green"} = + {3:"yellow", 4:True} + + """ + difference_dict = {} + for key in dict_a: + if key not in dict_b: + difference_dict[key] = dict_a[key] + + return difference_dict + + +def subtract(dict_a, dict_b, strict=False): + """a stricter form of subtract_by_key(), this version will only remove an + entry from dict_a if the key is in dict_b *and* the value at that key + matches""" + if not strict: + return subtract_by_key(dict_a, dict_b) + + difference_dict = {} + for key in dict_a: + if key not in dict_b or dict_b[key] != dict_a[key]: + difference_dict[key] = dict_a[key] + + return difference_dict + + +WinnowedResult = namedtuple("WinnowedResult", ['has', 'has_not']) +def winnow_by_keys(dct, keys=None, filter_func=None): + """separates a dict into has-keys and not-has-keys pairs, using either + a list of keys or a filtering function.""" + has = {} + has_not = {} + + for key in dct: + key_passes_check = False + if keys is not None: + key_passes_check = key in keys + elif filter_func is not None: + key_passes_check = filter_func(key) + + if key_passes_check: + has[key] = dct[key] + else: + has_not[key] = dct[key] + + return WinnowedResult(has, has_not) + + +def intersection(dict_a, dict_b, strict=True): + intersection_dict = {} + + for key in dict_a: + if key in dict_b: + if not strict or dict_a[key] == dict_b[key]: + intersection_dict[key] = dict_a[key] + + return intersection_dict + + +def setdefaults(dct, defaults): + """Given a target dct and a dict of {key:default value} pairs, + calls setdefault for all of those pairs.""" + for key in defaults: + dct.setdefault(key, defaults[key]) + + return dct diff --git a/game/python-extra/utils/dicts/limited_dict.py b/game/python-extra/utils/dicts/limited_dict.py new file mode 100644 index 0000000..c690118 --- /dev/null +++ b/game/python-extra/utils/dicts/limited_dict.py @@ -0,0 +1,40 @@ +from collections import MutableMapping + + +class LimitedDict(MutableMapping): + def __init__(self, args=None, **kwargs): + keys = kwargs.pop('keys', []) + self.__keys = keys + + self.__data = {} + + if args: + kwargs.update((key, val) for key, val in args) + + for key, val in kwargs.items(): + self[key] = val + + def __setitem__(self, key, val): + if key not in self.__keys: + raise KeyError("Illegal key: {}".format(key)) + + self.__data[key] = val + + def __getitem__(self, key): + return self.__data[key] + + def __iter__(self): + return self.__data.__iter__() + + def __delitem__(self, key): + del self.__data[key] + + def __len__(self): + return len(self.__data) + + def __repr__(self): + return "{}({}, {})".format(self.__class__.__name__, self.defined_keys, self.__data) + + @property + def defined_keys(self): + return self.__keys |