123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- from ._compat import Set, Hashable
- import sys
- from pyrsistent._pmap import pmap
- PY2 = sys.version_info[0] < 3
- class PSet(object):
- """
- Persistent set implementation. Built on top of the persistent map. The set supports all operations
- in the Set protocol and is Hashable.
- Do not instantiate directly, instead use the factory functions :py:func:`s` or :py:func:`pset`
- to create an instance.
- Random access and insert is log32(n) where n is the size of the set.
- Some examples:
- >>> s = pset([1, 2, 3, 1])
- >>> s2 = s.add(4)
- >>> s3 = s2.remove(2)
- >>> s
- pset([1, 2, 3])
- >>> s2
- pset([1, 2, 3, 4])
- >>> s3
- pset([1, 3, 4])
- """
- __slots__ = ('_map', '__weakref__')
- def __new__(cls, m):
- self = super(PSet, cls).__new__(cls)
- self._map = m
- return self
- def __contains__(self, element):
- return element in self._map
- def __iter__(self):
- return iter(self._map)
- def __len__(self):
- return len(self._map)
- def __repr__(self):
- if PY2 or not self:
- return 'p' + str(set(self))
- return 'pset([{0}])'.format(str(set(self))[1:-1])
- def __str__(self):
- return self.__repr__()
- def __hash__(self):
- return hash(self._map)
- def __reduce__(self):
- # Pickling support
- return pset, (list(self),)
- @classmethod
- def _from_iterable(cls, it, pre_size=8):
- return PSet(pmap(dict((k, True) for k in it), pre_size=pre_size))
- def add(self, element):
- """
- Return a new PSet with element added
- >>> s1 = s(1, 2)
- >>> s1.add(3)
- pset([1, 2, 3])
- """
- return self.evolver().add(element).persistent()
- def update(self, iterable):
- """
- Return a new PSet with elements in iterable added
- >>> s1 = s(1, 2)
- >>> s1.update([3, 4, 4])
- pset([1, 2, 3, 4])
- """
- e = self.evolver()
- for element in iterable:
- e.add(element)
- return e.persistent()
- def remove(self, element):
- """
- Return a new PSet with element removed. Raises KeyError if element is not present.
- >>> s1 = s(1, 2)
- >>> s1.remove(2)
- pset([1])
- """
- if element in self._map:
- return self.evolver().remove(element).persistent()
- raise KeyError("Element '%s' not present in PSet" % element)
- def discard(self, element):
- """
- Return a new PSet with element removed. Returns itself if element is not present.
- """
- if element in self._map:
- return self.evolver().remove(element).persistent()
- return self
- class _Evolver(object):
- __slots__ = ('_original_pset', '_pmap_evolver')
- def __init__(self, original_pset):
- self._original_pset = original_pset
- self._pmap_evolver = original_pset._map.evolver()
- def add(self, element):
- self._pmap_evolver[element] = True
- return self
- def remove(self, element):
- del self._pmap_evolver[element]
- return self
- def is_dirty(self):
- return self._pmap_evolver.is_dirty()
- def persistent(self):
- if not self.is_dirty():
- return self._original_pset
- return PSet(self._pmap_evolver.persistent())
- def __len__(self):
- return len(self._pmap_evolver)
- def copy(self):
- return self
- def evolver(self):
- """
- Create a new evolver for this pset. For a discussion on evolvers in general see the
- documentation for the pvector evolver.
- Create the evolver and perform various mutating updates to it:
- >>> s1 = s(1, 2, 3)
- >>> e = s1.evolver()
- >>> _ = e.add(4)
- >>> len(e)
- 4
- >>> _ = e.remove(1)
- The underlying pset remains the same:
- >>> s1
- pset([1, 2, 3])
- The changes are kept in the evolver. An updated pmap can be created using the
- persistent() function on the evolver.
- >>> s2 = e.persistent()
- >>> s2
- pset([2, 3, 4])
- The new pset will share data with the original pset in the same way that would have
- been done if only using operations on the pset.
- """
- return PSet._Evolver(self)
- # All the operations and comparisons you would expect on a set.
- #
- # This is not very beautiful. If we avoid inheriting from PSet we can use the
- # __slots__ concepts (which requires a new style class) and hopefully save some memory.
- __le__ = Set.__le__
- __lt__ = Set.__lt__
- __gt__ = Set.__gt__
- __ge__ = Set.__ge__
- __eq__ = Set.__eq__
- __ne__ = Set.__ne__
- __and__ = Set.__and__
- __or__ = Set.__or__
- __sub__ = Set.__sub__
- __xor__ = Set.__xor__
- issubset = __le__
- issuperset = __ge__
- union = __or__
- intersection = __and__
- difference = __sub__
- symmetric_difference = __xor__
- isdisjoint = Set.isdisjoint
- Set.register(PSet)
- Hashable.register(PSet)
- _EMPTY_PSET = PSet(pmap())
- def pset(iterable=(), pre_size=8):
- """
- Creates a persistent set from iterable. Optionally takes a sizing parameter equivalent to that
- used for :py:func:`pmap`.
- >>> s1 = pset([1, 2, 3, 2])
- >>> s1
- pset([1, 2, 3])
- """
- if not iterable:
- return _EMPTY_PSET
- return PSet._from_iterable(iterable, pre_size=pre_size)
- def s(*elements):
- """
- Create a persistent set.
- Takes an arbitrary number of arguments to insert into the new set.
- >>> s1 = s(1, 2, 3, 2)
- >>> s1
- pset([1, 2, 3])
- """
- return pset(elements)
|