setutils docs WIP

This commit is contained in:
Mahmoud Hashemi 2015-04-05 23:56:13 -07:00
parent e0070511ae
commit ba5bd15658
2 changed files with 42 additions and 24 deletions

View File

@ -4,10 +4,10 @@
The :class:`set` type brings brings the practical expressiveness of The :class:`set` type brings brings the practical expressiveness of
set theory to Python. It has a very rich API overall, but lacks a set theory to Python. It has a very rich API overall, but lacks a
couple of fundamental features. For one, sets are not ordered. On top couple of fundamental features. For one, sets are not ordered. On top
of this, sets are not indexable, i.e, my_set[8] will raise an of this, sets are not indexable, i.e, ``my_set[8]`` will raise an
exception. The :class:`IndexedSet` type remedies both of these issues :exc:`TypeError`. The :class:`IndexedSet` type remedies both of these
without compromising on the excellent complexity characteristics of issues without compromising on the excellent complexity
Python's builtin set implementation. characteristics of Python's built-in set implementation.
""" """
@ -37,11 +37,10 @@ _COMPACTION_FACTOR = 8
class IndexedSet(MutableSet): class IndexedSet(MutableSet):
"""\ """``IndexedSet`` is a :class:`collections.MutableSet` that maintains
IndexedSet is a :class:`collections.MutableSet` that maintains
insertion order and uniqueness of inserted elements. It's a hybrid insertion order and uniqueness of inserted elements. It's a hybrid
type, mostly like an OrderedSet, but also list-like, in that it type, mostly like an OrderedSet, but also :class:`list`-like, in
supports indexing and slicing: that it supports indexing and slicing:
>>> x = IndexedSet(range(4) + range(8)) >>> x = IndexedSet(range(4) + range(8))
>>> x >>> x
@ -54,7 +53,13 @@ class IndexedSet(MutableSet):
>>> ''.join(fcr[:fcr.index('.')]) >>> ''.join(fcr[:fcr.index('.')])
'frecditpo' 'frecditpo'
As you can see, the IndexedSet is almost like a UniqueList, Standard set operators and interoperation with :class:`set` are
all supported:
>>> fcr & set('cash4gold.com')
IndexedSet(['c', 'd', 'o', '.', 'm'])
As you can see, the ``IndexedSet`` is almost like a ``UniqueList``,
retaining only one copy of a given value, in the order it was retaining only one copy of a given value, in the order it was
first added. For the curious, the reason why IndexedSet does not first added. For the curious, the reason why IndexedSet does not
support setting items based on index (i.e, ``__setitem__()``), support setting items based on index (i.e, ``__setitem__()``),
@ -63,10 +68,10 @@ class IndexedSet(MutableSet):
my_indexed_set = [A, B, C, D] my_indexed_set = [A, B, C, D]
my_indexed_set[2] = A my_indexed_set[2] = A
At this point, a set requires only one A, but a list would At this point, a set requires only one *A*, but a :class:`list` would
overwrite C. Overwriting C would change the length of the list, overwrite *C*. Overwriting *C* would change the length of the list,
meaning that my_indexed_set[2] would not be A, as expected with a meaning that ``my_indexed_set[2]`` would not be *A*, as expected with a
list, but rather D. So, no __setitem__(). list, but rather *D*. So, no ``__setitem__()``.
Otherwise, the API strives to be as complete a union of the Otherwise, the API strives to be as complete a union of the
:class:`list` and :class:`set` APIs as possible. :class:`list` and :class:`set` APIs as possible.
@ -179,17 +184,18 @@ class IndexedSet(MutableSet):
@classmethod @classmethod
def from_iterable(cls, it): def from_iterable(cls, it):
"from_iterable(it) -> create a set from an iterable"
return cls(it) return cls(it)
# set operations # set operations
def add(self, item): def add(self, item):
"set.add(item) -> add item to the set" "add(item) -> add item to the set"
if item not in self.item_index_map: if item not in self.item_index_map:
self.item_index_map[item] = len(self.item_list) self.item_index_map[item] = len(self.item_list)
self.item_list.append(item) self.item_list.append(item)
def remove(self, item): def remove(self, item):
"set.remove(item) -> remove item from the set, raises if not present" "remove(item) -> remove item from the set, raises if not present"
try: try:
didx = self.item_index_map.pop(item) didx = self.item_index_map.pop(item)
except KeyError: except KeyError:
@ -199,19 +205,20 @@ class IndexedSet(MutableSet):
self._cull() self._cull()
def discard(self, item): def discard(self, item):
"set.discard(item) -> discard item from the set (does not raise)" "discard(item) -> discard item from the set (does not raise)"
try: try:
self.remove(item) self.remove(item)
except KeyError: except KeyError:
pass pass
def clear(self): def clear(self):
"set.clear() -> empty the set" "clear() -> empty the set"
del self.item_list[:] del self.item_list[:]
del self.dead_indices[:] del self.dead_indices[:]
self.item_index_map.clear() self.item_index_map.clear()
def isdisjoint(self, other): def isdisjoint(self, other):
"isdisjoint(other) -> return True if no overlap with other"
iim = self.item_index_map iim = self.item_index_map
for k in other: for k in other:
if k in iim: if k in iim:
@ -219,6 +226,7 @@ class IndexedSet(MutableSet):
return True return True
def issubset(self, other): def issubset(self, other):
"issubset(other) -> return True if other contains this set"
if len(other) < len(self): if len(other) < len(self):
return False return False
for k in self.item_index_map: for k in self.item_index_map:
@ -227,6 +235,7 @@ class IndexedSet(MutableSet):
return True return True
def issuperset(self, other): def issuperset(self, other):
"issuperset(other) -> return True if set contains other"
if len(other) > len(self): if len(other) > len(self):
return False return False
iim = self.item_index_map iim = self.item_index_map
@ -236,9 +245,11 @@ class IndexedSet(MutableSet):
return True return True
def union(self, *others): def union(self, *others):
"union(*others) -> return a new set containing this set and others"
return self.from_iterable(chain(self, *others)) return self.from_iterable(chain(self, *others))
def iter_intersection(self, *others): def iter_intersection(self, *others):
"iter_intersection(*others) -> iterate over elements also in others"
for k in self: for k in self:
for other in others: for other in others:
if k not in other: if k not in other:
@ -248,12 +259,14 @@ class IndexedSet(MutableSet):
return return
def intersection(self, *others): def intersection(self, *others):
"intersection(*others) -> get a set with overlap of this and others"
if len(others) == 1: if len(others) == 1:
other = others[0] other = others[0]
return self.from_iterable(k for k in self if k in other) return self.from_iterable(k for k in self if k in other)
return self.from_iterable(self.iter_intersection(*others)) return self.from_iterable(self.iter_intersection(*others))
def iter_difference(self, *others): def iter_difference(self, *others):
"iter_difference(*others) -> iterate over elements not in others"
for k in self: for k in self:
for other in others: for other in others:
if k in other: if k in other:
@ -263,12 +276,14 @@ class IndexedSet(MutableSet):
return return
def difference(self, *others): def difference(self, *others):
"difference(*others) -> get a new set with elements not in others"
if len(others) == 1: if len(others) == 1:
other = others[0] other = others[0]
return self.from_iterable(k for k in self if k not in other) return self.from_iterable(k for k in self if k not in other)
return self.from_iterable(self.iter_difference(*others)) return self.from_iterable(self.iter_difference(*others))
def symmetric_difference(self, *others): def symmetric_difference(self, *others):
"symmetric_difference(*others) -> XOR set of this and others"
ret = self.union(*others) ret = self.union(*others)
return ret.difference(self.intersection(*others)) return ret.difference(self.intersection(*others))
@ -279,7 +294,7 @@ class IndexedSet(MutableSet):
# in-place set operations # in-place set operations
def update(self, *others): def update(self, *others):
"set.update() -> add values from one or more iterables" "update(*others) -> add values from one or more iterables"
if not others: if not others:
return # raise? return # raise?
elif len(others) == 1: elif len(others) == 1:
@ -290,16 +305,19 @@ class IndexedSet(MutableSet):
self.add(o) self.add(o)
def intersection_update(self, *others): def intersection_update(self, *others):
"intersection_update(*others) -> discard self.difference(*others)"
for val in self.difference(*others): for val in self.difference(*others):
self.discard(val) self.discard(val)
def difference_update(self, *others): def difference_update(self, *others):
"difference_update(*others) -> discard self.intersection(*others)"
if self in others: if self in others:
self.clear() self.clear()
for val in self.intersection(*others): for val in self.intersection(*others):
self.discard(val) self.discard(val)
def symmetric_difference_update(self, other): # note singular 'other' def symmetric_difference_update(self, other): # note singular 'other'
"symmetric_difference_update(other) -> in-place XOR with other"
if self is other: if self is other:
self.clear() self.clear()
for val in other: for val in other:
@ -325,6 +343,7 @@ class IndexedSet(MutableSet):
return self return self
def iter_slice(self, start, stop, step=None): def iter_slice(self, start, stop, step=None):
"iterate over a slice of the set"
iterable = self iterable = self
if start is not None: if start is not None:
start = self._get_real_index(start) start = self._get_real_index(start)
@ -354,7 +373,7 @@ class IndexedSet(MutableSet):
return ret return ret
def pop(self, index=None): def pop(self, index=None):
"list.pop(index) -> remove the item at a given index (-1 by default)" "pop(index) -> remove the item at a given index (-1 by default)"
item_index_map = self.item_index_map item_index_map = self.item_index_map
len_self = len(item_index_map) len_self = len(item_index_map)
if index is None or index == -1 or index == len_self - 1: if index is None or index == -1 or index == len_self - 1:
@ -370,13 +389,13 @@ class IndexedSet(MutableSet):
return ret return ret
def count(self, val): def count(self, val):
"list.count(val) -> count number of instances of value (0 or 1)" "count(val) -> count number of instances of value (0 or 1)"
if val in self.item_index_map: if val in self.item_index_map:
return 1 return 1
return 0 return 0
def reverse(self): def reverse(self):
"list.reverse() -> reverse the contents of the set in-place" "reverse() -> reverse the contents of the set in-place"
reversed_list = list(reversed(self)) reversed_list = list(reversed(self))
self.item_list[:] = reversed_list self.item_list[:] = reversed_list
for i, item in enumerate(self.item_list): for i, item in enumerate(self.item_list):
@ -384,7 +403,7 @@ class IndexedSet(MutableSet):
del self.dead_indices[:] del self.dead_indices[:]
def sort(self): def sort(self):
"list.sort() -> sort the contents of the set in-place" "sort() -> sort the contents of the set in-place"
sorted_list = sorted(self) sorted_list = sorted(self)
if sorted_list == self.item_list: if sorted_list == self.item_list:
return return
@ -394,7 +413,7 @@ class IndexedSet(MutableSet):
del self.dead_indices[:] del self.dead_indices[:]
def index(self, val): def index(self, val):
"list.index(val) -> get the index of a value, raises if not present" "index(val) -> get the index of a value, raises if not present"
try: try:
return self.item_index_map[val] return self.item_index_map[val]
except KeyError: except KeyError:

View File

@ -3,4 +3,3 @@
.. automodule:: boltons.setutils .. automodule:: boltons.setutils
:members: :members:
:undoc-members: