Source code for utool.util_set

# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
from six.moves import zip, map, range  # NOQA
import collections
import weakref
from utool import util_inject

print, rrr, profile = util_inject.inject2(__name__)


class _Link(object):
    __slots__ = ('prev', 'next', 'key', '__weakref__')


[docs]class OrderedSet(collections.MutableSet): """ Set the remembers the order elements were added Big-O running times for all methods are the same as for regular sets. The internal self._map dictionary maps keys to links in a doubly linked list. The circular doubly linked list starts and ends with a sentinel element. The sentinel element never gets deleted (this simplifies the algorithm). The prev/next links are weakref proxies (to prevent circular references). Individual links are kept alive by the hard reference in self._map. Those hard references disappear when a key is deleted from an OrderedSet. References: http://code.activestate.com/recipes/576696/ http://code.activestate.com/recipes/576694/ http://stackoverflow.com/questions/1653970/does-python-have-an-ordered-set """ def __init__(self, iterable=None): self._root = root = _Link() # sentinel node for doubly linked list root.prev = root.next = root self._map = {} # key --> link if iterable is not None: self |= iterable def __len__(self): return len(self._map) def __contains__(self, key): return key in self._map
[docs] def add(self, key): """ Store new key in a new link at the end of the linked list """ if key not in self._map: self._map[key] = link = _Link() root = self._root last = root.prev link.prev, link.next, link.key = last, root, key last.next = root.prev = weakref.proxy(link)
[docs] def append(self, key): """ Alias for add """ return self.add(key)
[docs] def discard(self, key): # Remove an existing item using self._map to find the link which is # then removed by updating the links in the predecessor and successors. if key in self._map: link = self._map.pop(key) link.prev.next = link.next link.next.prev = link.prev
def __iter__(self): # Traverse the linked list in order. root = self._root curr = root.next while curr is not root: yield curr.key curr = curr.next def __reversed__(self): # Traverse the linked list in reverse order. root = self._root curr = root.prev while curr is not root: yield curr.key curr = curr.prev
[docs] def pop(self, last=True): if not self: raise KeyError('set is empty') key = next(reversed(self)) if last else next(iter(self)) self.discard(key) return key
def __repr__(self): if not self: return '%s()' % (self.__class__.__name__,) return '%s(%r)' % (self.__class__.__name__, list(self)) def __eq__(self, other): if isinstance(other, OrderedSet): return len(self) == len(other) and list(self) == list(other) return not self.isdisjoint(other)
[docs] @classmethod def union(cls, *sets): """ >>> from utool.util_set import * # NOQA """ import utool as ut lists_ = ut.flatten([list(s) for s in sets]) return cls(lists_)
[docs] def update(self, other): """ union update """ for item in other: self.add(item)
def __getitem__(self, index): """ Example: >>> # ENABLE_DOCTEST >>> import utool as ut >>> self = ut.oset([1, 2, 3]) >>> assert self[0] == 1 >>> assert self[1] == 2 >>> assert self[2] == 3 >>> ut.assert_raises(IndexError, self.__getitem__, 3) >>> assert self[-1] == 3 >>> assert self[-2] == 2 >>> assert self[-3] == 1 >>> ut.assert_raises(IndexError, self.__getitem__, -4) """ if index < 0: iter_ = self.__reversed__ index_ = -1 - index else: index_ = index iter_ = self.__iter__ if index_ >= len(self): raise IndexError('index %r out of range %r' % (index, len(self))) for count, item in zip(range(index_ + 1), iter_()): pass return item
[docs] def index(self, item): """ Find the index of `item` in the OrderedSet Example: >>> # ENABLE_DOCTEST >>> import utool as ut >>> self = ut.oset([1, 2, 3]) >>> assert self.index(1) == 0 >>> assert self.index(2) == 1 >>> assert self.index(3) == 2 >>> ut.assert_raises(ValueError, self.index, 4) """ for count, other in enumerate(self): if item == other: return count raise ValueError('%r is not in OrderedSet' % (item,))
# alias oset = OrderedSet