mirror of https://github.com/mahmoud/boltons.git
setutils docs WIP
This commit is contained in:
parent
e0070511ae
commit
ba5bd15658
|
@ -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:
|
||||||
|
|
|
@ -3,4 +3,3 @@
|
||||||
|
|
||||||
.. automodule:: boltons.setutils
|
.. automodule:: boltons.setutils
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
|
||||||
|
|
Loading…
Reference in New Issue