def __init__(self, iterable=None, key=None, load=1000, _set=None): """ A `SortedSet` provides the same methods as a `set`. Additionally, a `SortedSet` maintains its items in sorted order, allowing the `SortedSet` to be indexed. An optional *iterable* provides an initial series of items to populate the `SortedSet`. An optional *key* argument defines a callable that, like the `key` argument to Python's `sorted` function, extracts a comparison key from each set item. If no function is specified, the default compares the set items directly. An optional *load* specifies the load-factor of the set. The default load factor of '1000' works well for sets from tens to tens of millions of elements. Good practice is to use a value that is the cube root of the set size. With billions of elements, the best load factor depends on your usage. It's best to leave the load factor at the default until you start benchmarking. """ self._key = key self._load = load self._set = set() if _set is None else _set _set = self._set self.isdisjoint = _set.isdisjoint self.issubset = _set.issubset self.issuperset = _set.issuperset if key is None: self._list = SortedList(self._set, load=load) else: self._list = SortedListWithKey(self._set, key=key, load=load) _list = self._list self.bisect_left = _list.bisect_left self.bisect = _list.bisect self.bisect_right = _list.bisect_right self.index = _list.index self.irange = _list.irange self.islice = _list.islice if key is not None: self.bisect_key_left = _list.bisect_key_left self.bisect_key_right = _list.bisect_key_right self.bisect_key = _list.bisect_key self.irange_key = _list.irange_key if iterable is not None: self._update(iterable)
def __init__(self, *args, **kwargs): """ A SortedDict provides the same methods as a dict. Additionally, a SortedDict efficiently maintains its keys in sorted order. Consequently, the keys method will return the keys in sorted order, the popitem method will remove the item with the highest key, etc. An optional *key* argument defines a callable that, like the `key` argument to Python's `sorted` function, extracts a comparison key from each dict key. If no function is specified, the default compares the dict keys directly. The `key` argument must be provided as a positional argument and must come before all other arguments. An optional *load* argument defines the load factor of the internal list used to maintain sort order. If present, this argument must come before an iterable. The default load factor of '1000' works well for lists from tens to tens of millions of elements. Good practice is to use a value that is the cube root of the list size. With billions of elements, the best load factor depends on your usage. It's best to leave the load factor at the default until you start benchmarking. An optional *iterable* argument provides an initial series of items to populate the SortedDict. Each item in the series must itself contain two items. The first is used as a key in the new dictionary, and the second as the key's value. If a given key is seen more than once, the last value associated with it is retained in the new dictionary. If keyword arguments are given, the keywords themselves with their associated values are added as items to the dictionary. If a key is specified both in the positional argument and as a keyword argument, the value associated with the keyword is retained in the dictionary. For example, these all return a dictionary equal to ``{"one": 2, "two": 3}``: * ``SortedDict(one=2, two=3)`` * ``SortedDict({'one': 2, 'two': 3})`` * ``SortedDict(zip(('one', 'two'), (2, 3)))`` * ``SortedDict([['two', 3], ['one', 2]])`` The first example only works for keys that are valid Python identifiers; the others work with any valid keys. """ if len(args) > 0 and (args[0] is None or callable(args[0])): self._key = args[0] args = args[1:] else: self._key = None if len(args) > 0 and type(args[0]) == int: self._load = args[0] args = args[1:] else: self._load = 1000 if self._key is None: self._list = SortedList(load=self._load) else: self._list = SortedListWithKey(key=self._key, load=self._load) # Cache function pointers to dict methods. _dict = super(SortedDict, self) self._dict = _dict self._clear = _dict.clear self._delitem = _dict.__delitem__ self._iter = _dict.__iter__ self._pop = _dict.pop self._setdefault = _dict.setdefault self._setitem = _dict.__setitem__ self._dict_update = _dict.update # Cache function pointers to SortedList methods. _list = self._list self._list_add = _list.add self.bisect_left = _list.bisect_left self.bisect = _list.bisect_right self.bisect_right = _list.bisect_right self._list_clear = _list.clear self.index = _list.index self._list_pop = _list.pop self._list_remove = _list.remove self._list_update = _list.update self.irange = _list.irange self.islice = _list.islice if self._key is not None: self.bisect_key_left = _list.bisect_key_left self.bisect_key_right = _list.bisect_key_right self.bisect_key = _list.bisect_key self.irange_key = _list.irange_key self.iloc = _IlocWrapper(self) self._update(*args, **kwargs)
class SortedSet(MutableSet, Sequence): """ A `SortedSet` provides the same methods as a `set`. Additionally, a `SortedSet` maintains its items in sorted order, allowing the `SortedSet` to be indexed. Unlike a `set`, a `SortedSet` requires items be hashable and comparable. """ def __init__(self, iterable=None, key=None, load=1000, _set=None): """ A `SortedSet` provides the same methods as a `set`. Additionally, a `SortedSet` maintains its items in sorted order, allowing the `SortedSet` to be indexed. An optional *iterable* provides an initial series of items to populate the `SortedSet`. An optional *key* argument defines a callable that, like the `key` argument to Python's `sorted` function, extracts a comparison key from each set item. If no function is specified, the default compares the set items directly. An optional *load* specifies the load-factor of the set. The default load factor of '1000' works well for sets from tens to tens of millions of elements. Good practice is to use a value that is the cube root of the set size. With billions of elements, the best load factor depends on your usage. It's best to leave the load factor at the default until you start benchmarking. """ self._key = key self._load = load self._set = set() if _set is None else _set _set = self._set self.isdisjoint = _set.isdisjoint self.issubset = _set.issubset self.issuperset = _set.issuperset if key is None: self._list = SortedList(self._set, load=load) else: self._list = SortedListWithKey(self._set, key=key, load=load) _list = self._list self.bisect_left = _list.bisect_left self.bisect = _list.bisect self.bisect_right = _list.bisect_right self.index = _list.index self.irange = _list.irange self.islice = _list.islice if key is not None: self.bisect_key_left = _list.bisect_key_left self.bisect_key_right = _list.bisect_key_right self.bisect_key = _list.bisect_key self.irange_key = _list.irange_key if iterable is not None: self._update(iterable) def __contains__(self, value): """Return True if and only if *value* is an element in the set.""" return (value in self._set) def __getitem__(self, index): """ Return the element at position *index*. Supports slice notation and negative indexes. """ return self._list[index] def __delitem__(self, index): """ Remove the element at position *index*. Supports slice notation and negative indexes. """ _list = self._list if isinstance(index, slice): values = _list[index] self._set.difference_update(values) else: value = _list[index] self._set.remove(value) del _list[index] def _make_cmp(set_op, doc): def comparer(self, that): if isinstance(that, SortedSet): return set_op(self._set, that._set) elif isinstance(that, Set): return set_op(self._set, that) else: return NotImplemented comparer.__name__ = '__{0}__'.format(set_op.__name__) doc_str = 'Return True if and only if Set is {0} `that`.' comparer.__doc__ = doc_str.format(doc) return comparer __eq__ = _make_cmp(op.eq, 'equal to') __ne__ = _make_cmp(op.ne, 'not equal to') __lt__ = _make_cmp(op.lt, 'a proper subset of') __gt__ = _make_cmp(op.gt, 'a proper superset of') __le__ = _make_cmp(op.le, 'a subset of') __ge__ = _make_cmp(op.ge, 'a superset of') def __len__(self): """Return the number of elements in the set.""" return len(self._set) def __iter__(self): """ Return an iterator over the Set. Elements are iterated in their sorted order. Iterating the Set while adding or deleting values may raise a `RuntimeError` or fail to iterate over all entries. """ return iter(self._list) def __reversed__(self): """ Return an iterator over the Set. Elements are iterated in their reverse sorted order. Iterating the Set while adding or deleting values may raise a `RuntimeError` or fail to iterate over all entries. """ return reversed(self._list) def add(self, value): """Add the element *value* to the set.""" if value not in self._set: self._set.add(value) self._list.add(value) def clear(self): """Remove all elements from the set.""" self._set.clear() self._list.clear() def copy(self): """Create a shallow copy of the sorted set.""" return self.__class__(key=self._key, load=self._load, _set=set(self._set)) __copy__ = copy def count(self, value): """Return the number of occurrences of *value* in the set.""" return 1 if value in self._set else 0 def discard(self, value): """ Remove the first occurrence of *value*. If *value* is not a member, does nothing. """ if value in self._set: self._set.remove(value) self._list.discard(value) def pop(self, index=-1): """ Remove and return item at *index* (default last). Raises IndexError if set is empty or index is out of range. Negative indexes are supported, as for slice indices. """ value = self._list.pop(index) self._set.remove(value) return value def remove(self, value): """ Remove first occurrence of *value*. Raises ValueError if *value* is not present. """ self._set.remove(value) self._list.remove(value) def difference(self, *iterables): """ Return a new set with elements in the set that are not in the *iterables*. """ diff = self._set.difference(*iterables) new_set = self.__class__(key=self._key, load=self._load, _set=diff) return new_set __sub__ = difference __rsub__ = __sub__ def difference_update(self, *iterables): """ Update the set, removing elements found in keeping only elements found in any of the *iterables*. """ values = set(chain(*iterables)) if (4 * len(values)) > len(self): self._set.difference_update(values) self._list.clear() self._list.update(self._set) else: _discard = self.discard for value in values: _discard(value) return self __isub__ = difference_update def intersection(self, *iterables): """ Return a new set with elements common to the set and all *iterables*. """ comb = self._set.intersection(*iterables) new_set = self.__class__(key=self._key, load=self._load, _set=comb) return new_set __and__ = intersection __rand__ = __and__ def intersection_update(self, *iterables): """ Update the set, keeping only elements found in it and all *iterables*. """ self._set.intersection_update(*iterables) self._list.clear() self._list.update(self._set) return self __iand__ = intersection_update def symmetric_difference(self, that): """ Return a new set with elements in either *self* or *that* but not both. """ diff = self._set.symmetric_difference(that) new_set = self.__class__(key=self._key, load=self._load, _set=diff) return new_set __xor__ = symmetric_difference __rxor__ = __xor__ def symmetric_difference_update(self, that): """ Update the set, keeping only elements found in either *self* or *that*, but not in both. """ self._set.symmetric_difference_update(that) self._list.clear() self._list.update(self._set) return self __ixor__ = symmetric_difference_update def union(self, *iterables): """ Return a new SortedSet with elements from the set and all *iterables*. """ return self.__class__( chain( iter(self), *iterables), key=self._key, load=self._load) __or__ = union __ror__ = __or__ def update(self, *iterables): """Update the set, adding elements from all *iterables*.""" values = set(chain(*iterables)) if (4 * len(values)) > len(self): self._set.update(values) self._list.clear() self._list.update(self._set) else: _add = self.add for value in values: _add(value) return self __ior__ = update _update = update def __reduce__(self): return (self.__class__, ((), self._key, self._load, self._set)) @recursive_repr def __repr__(self): temp = '{0}({1}, key={2}, load={3})' return temp.format(self.__class__.__name__, repr(list(self)), repr(self._key), repr(self._load)) def _check(self): self._list._check() assert len(self._set) == len(self._list) _set = self._set assert all(val in _set for val in self._list)
class SortedDict(dict): """ A SortedDict provides the same methods as a dict. Additionally, a SortedDict efficiently maintains its keys in sorted order. Consequently, the keys method will return the keys in sorted order, the popitem method will remove the item with the highest key, etc. """ def __init__(self, *args, **kwargs): """ A SortedDict provides the same methods as a dict. Additionally, a SortedDict efficiently maintains its keys in sorted order. Consequently, the keys method will return the keys in sorted order, the popitem method will remove the item with the highest key, etc. An optional *key* argument defines a callable that, like the `key` argument to Python's `sorted` function, extracts a comparison key from each dict key. If no function is specified, the default compares the dict keys directly. The `key` argument must be provided as a positional argument and must come before all other arguments. An optional *load* argument defines the load factor of the internal list used to maintain sort order. If present, this argument must come before an iterable. The default load factor of '1000' works well for lists from tens to tens of millions of elements. Good practice is to use a value that is the cube root of the list size. With billions of elements, the best load factor depends on your usage. It's best to leave the load factor at the default until you start benchmarking. An optional *iterable* argument provides an initial series of items to populate the SortedDict. Each item in the series must itself contain two items. The first is used as a key in the new dictionary, and the second as the key's value. If a given key is seen more than once, the last value associated with it is retained in the new dictionary. If keyword arguments are given, the keywords themselves with their associated values are added as items to the dictionary. If a key is specified both in the positional argument and as a keyword argument, the value associated with the keyword is retained in the dictionary. For example, these all return a dictionary equal to ``{"one": 2, "two": 3}``: * ``SortedDict(one=2, two=3)`` * ``SortedDict({'one': 2, 'two': 3})`` * ``SortedDict(zip(('one', 'two'), (2, 3)))`` * ``SortedDict([['two', 3], ['one', 2]])`` The first example only works for keys that are valid Python identifiers; the others work with any valid keys. """ if len(args) > 0 and (args[0] is None or callable(args[0])): self._key = args[0] args = args[1:] else: self._key = None if len(args) > 0 and type(args[0]) == int: self._load = args[0] args = args[1:] else: self._load = 1000 if self._key is None: self._list = SortedList(load=self._load) else: self._list = SortedListWithKey(key=self._key, load=self._load) # Cache function pointers to dict methods. _dict = super(SortedDict, self) self._dict = _dict self._clear = _dict.clear self._delitem = _dict.__delitem__ self._iter = _dict.__iter__ self._pop = _dict.pop self._setdefault = _dict.setdefault self._setitem = _dict.__setitem__ self._dict_update = _dict.update # Cache function pointers to SortedList methods. _list = self._list self._list_add = _list.add self.bisect_left = _list.bisect_left self.bisect = _list.bisect_right self.bisect_right = _list.bisect_right self._list_clear = _list.clear self.index = _list.index self._list_pop = _list.pop self._list_remove = _list.remove self._list_update = _list.update self.irange = _list.irange self.islice = _list.islice if self._key is not None: self.bisect_key_left = _list.bisect_key_left self.bisect_key_right = _list.bisect_key_right self.bisect_key = _list.bisect_key self.irange_key = _list.irange_key self.iloc = _IlocWrapper(self) self._update(*args, **kwargs) def clear(self): """Remove all elements from the dictionary.""" self._clear() self._list_clear() def __delitem__(self, key): """ Remove ``d[key]`` from *d*. Raises a KeyError if *key* is not in the dictionary. """ self._delitem(key) self._list_remove(key) def __iter__(self): """ Return an iterator over the sorted keys of the dictionary. Iterating the Mapping while adding or deleting keys may raise a `RuntimeError` or fail to iterate over all entries. """ return iter(self._list) def __reversed__(self): """ Return a reversed iterator over the sorted keys of the dictionary. Iterating the Mapping while adding or deleting keys may raise a `RuntimeError` or fail to iterate over all entries. """ return reversed(self._list) def __setitem__(self, key, value): """Set `d[key]` to *value*.""" if key not in self: self._list_add(key) self._setitem(key, value) def copy(self): """Return a shallow copy of the sorted dictionary.""" return self.__class__(self._key, self._load, self._iteritems()) __copy__ = copy @classmethod def fromkeys(cls, seq, value=None): """ Create a new dictionary with keys from *seq* and values set to *value*. """ return cls((key, value) for key in seq) if hexversion < 0x03000000: def items(self): """ Return a list of the dictionary's items (``(key, value)`` pairs). """ return list(self._iteritems()) else: def items(self): """ Return a new ItemsView of the dictionary's items. In addition to the methods provided by the built-in `view` the ItemsView is indexable (e.g. ``d.items()[5]``). """ return ItemsView(self) def iteritems(self): """ Return an iterator over the items (``(key, value)`` pairs). Iterating the Mapping while adding or deleting keys may raise a `RuntimeError` or fail to iterate over all entries. """ return iter((key, self[key]) for key in self._list) _iteritems = iteritems if hexversion < 0x03000000: def keys(self): """Return a SortedSet of the dictionary's keys.""" return SortedSet(self._list, key=self._key, load=self._load) else: def keys(self): """ Return a new KeysView of the dictionary's keys. In addition to the methods provided by the built-in `view` the KeysView is indexable (e.g. ``d.keys()[5]``). """ return KeysView(self) def iterkeys(self): """ Return an iterator over the sorted keys of the Mapping. Iterating the Mapping while adding or deleting keys may raise a `RuntimeError` or fail to iterate over all entries. """ return iter(self._list) if hexversion < 0x03000000: def values(self): """Return a list of the dictionary's values.""" return list(self._itervalues()) else: def values(self): """ Return a new :class:`ValuesView` of the dictionary's values. In addition to the methods provided by the built-in `view` the ValuesView is indexable (e.g., ``d.values()[5]``). """ return ValuesView(self) def itervalues(self): """ Return an iterator over the values of the Mapping. Iterating the Mapping while adding or deleting keys may raise a `RuntimeError` or fail to iterate over all entries. """ return iter(self[key] for key in self._list) _itervalues = itervalues def pop(self, key, default=_NotGiven): """ If *key* is in the dictionary, remove it and return its value, else return *default*. If *default* is not given and *key* is not in the dictionary, a KeyError is raised. """ if key in self: self._list_remove(key) return self._pop(key) else: if default is _NotGiven: raise KeyError(key) else: return default def popitem(self, last=True): """ Remove and return a ``(key, value)`` pair from the dictionary. If last=True (default) then remove the *greatest* `key` from the diciontary. Else, remove the *least* key from the dictionary. If the dictionary is empty, calling `popitem` raises a KeyError`. """ if not len(self): raise KeyError('popitem(): dictionary is empty') key = self._list_pop(-1 if last else 0) value = self._pop(key) return (key, value) def setdefault(self, key, default=None): """ If *key* is in the dictionary, return its value. If not, insert *key* with a value of *default* and return *default*. *default* defaults to ``None``. """ if key in self: return self[key] else: self._setitem(key, default) self._list_add(key) return default def update(self, *args, **kwargs): """ Update the dictionary with the key/value pairs from *other*, overwriting existing keys. *update* accepts either another dictionary object or an iterable of key/value pairs (as a tuple or other iterable of length two). If keyword arguments are specified, the dictionary is then updated with those key/value pairs: ``d.update(red=1, blue=2)``. """ if not len(self): self._dict_update(*args, **kwargs) self._list_update(self._iter()) return if (len(kwargs) == 0 and len(args) == 1 and isinstance(args[0], dict)): pairs = args[0] else: pairs = dict(*args, **kwargs) if (10 * len(pairs)) > len(self): self._dict_update(pairs) self._list_clear() self._list_update(self._iter()) else: for key in pairs: self[key] = pairs[key] _update = update @not26 def viewkeys(self): """ In Python 2.7 and later, return a new `KeysView` of the dictionary's keys. In Python 2.6, raise a NotImplementedError. """ return KeysView(self) @not26 def viewvalues(self): """ In Python 2.7 and later, return a new `ValuesView` of the dictionary's values. In Python 2.6, raise a NotImplementedError. """ return ValuesView(self) @not26 def viewitems(self): """ In Python 2.7 and later, return a new `ItemsView` of the dictionary's items. In Python 2.6, raise a NotImplementedError. """ return ItemsView(self) def __reduce__(self): return (self.__class__, (self._key, self._load, list(self._iteritems()))) @recursive_repr def __repr__(self): temp = '{0}({1}, {2}, {{{3}}})' items = ', '.join('{0}: {1}'.format( repr(key), repr(self[key])) for key in self._list) return temp.format(self.__class__.__name__, repr(self._key), repr(self._load), items) def _check(self): self._list._check() assert len(self) == len(self._list) assert all(val in self for val in self._list)