Skip to content

Commit 7990529

Browse files
committed
-
1 parent 6022353 commit 7990529

File tree

6 files changed

+115
-98
lines changed

6 files changed

+115
-98
lines changed

source_py3/python_toolbox/nifty_collections/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44
'''Defines various data types, similarly to the stdlib's `collections`.'''
55

66
from .ordered_dict import OrderedDict
7-
from .ordered_set import OrderedSet
7+
from .various_ordered_sets import OrderedSet, FrozenOrderedSet, EmittingOrderedSet
88
from .weak_key_default_dict import WeakKeyDefaultDict
99
from .weak_key_identity_dict import WeakKeyIdentityDict
1010
from .lazy_tuple import LazyTuple
11-
from .frozen_dict_and_frozen_ordered_dict import FrozenDict, FrozenOrderedDict
11+
from .various_frozen_dicts import FrozenDict, FrozenOrderedDict
1212
from .bagging import Bag, OrderedBag, FrozenBag, FrozenOrderedBag
1313
from .frozen_bag_bag import FrozenBagBag
1414
from .cute_enum import CuteEnum
1515

16-
from .emitting_ordered_set import EmittingOrderedSet
1716
from .emitting_weak_key_default_dict import EmittingWeakKeyDefaultDict
1817

1918
from .abstract import Ordered, DefinitelyUnordered

source_py3/python_toolbox/nifty_collections/bagging.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
from .lazy_tuple import LazyTuple
1616
from .ordered_dict import OrderedDict
17-
from .ordered_set import OrderedSet
18-
from .frozen_dict_and_frozen_ordered_dict import FrozenDict, FrozenOrderedDict
17+
from .various_ordered_sets import OrderedSet
18+
from .various_frozen_dicts import FrozenDict, FrozenOrderedDict
1919
from .abstract import Ordered, DefinitelyUnordered
2020

2121

source_py3/python_toolbox/nifty_collections/emitting_ordered_set.py

Lines changed: 0 additions & 52 deletions
This file was deleted.

source_py3/python_toolbox/nifty_collections/frozen_dict_and_frozen_ordered_dict.py renamed to source_py3/python_toolbox/nifty_collections/various_frozen_dicts.py

File renamed without changes.

source_py3/python_toolbox/nifty_collections/ordered_set.py renamed to source_py3/python_toolbox/nifty_collections/various_ordered_sets.py

Lines changed: 110 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,45 @@
11
# Copyright 2009-2015 Ram Rachum.
22
# This program is distributed under the MIT license.
33

4-
'''
5-
Defines the `OrderedSet` class.
6-
7-
See its documentation for more details.
8-
'''
9-
# todo: revamp
10-
114
import collections
125

136
from python_toolbox import comparison_tools
147

15-
168
KEY, PREV, NEXT = range(3)
179

1810

19-
class OrderedSet(collections.MutableSet, collections.Sequence):
20-
'''
21-
A set with an order.
22-
23-
You can also think of this as a list which doesn't allow duplicate items
24-
and whose `__contains__` method is O(1).
11+
class BaseOrderedSet(collections.Set, collections.Sequence):
2512
'''
13+
Base class for `OrderedSet` and `FrozenOrderedSet`, i.e. set with an order.
2614
15+
This behaves like a `set` except items have an order. (By default they're
16+
ordered by insertion order, but that order can be changed.)
17+
'''
18+
2719
def __init__(self, iterable=None):
2820
self.clear()
2921
if iterable is not None:
3022
self |= iterable
3123

32-
33-
def clear(self):
34-
self._end = []
35-
self._end += [None, self._end, self._end]
36-
self._map = {}
37-
38-
3924
def __getitem__(self, index):
4025
for i, item in enumerate(self):
4126
if i == index:
4227
return item
4328
else:
4429
raise IndexError
4530

46-
4731
def __len__(self):
4832
return len(self._map)
4933

5034
def __contains__(self, key):
5135
return key in self._map
5236

5337
def add(self, key):
54-
"""
38+
'''
5539
Add an element to a set.
5640
5741
This has no effect if the element is already present.
58-
"""
42+
'''
5943
if key not in self._map:
6044
end = self._end
6145
curr = end[PREV]
@@ -86,28 +70,36 @@ def __reversed__(self):
8670
yield curr[KEY]
8771
curr = curr[PREV]
8872

89-
def pop(self, last=True):
90-
"""Remove and return an arbitrary set element."""
91-
if not self:
92-
raise KeyError('set is empty')
93-
key = next(reversed(self) if last else iter(self))
94-
self.discard(key)
95-
return key
96-
9773
def __repr__(self):
9874
if not self:
9975
return '%s()' % (self.__class__.__name__,)
10076
return '%s(%r)' % (self.__class__.__name__, list(self))
10177

10278
def __eq__(self, other):
103-
if isinstance(other, OrderedSet):
104-
return len(self) == len(other) and list(self) == list(other)
105-
return set(self) == set(other)
79+
return (
80+
isinstance(other, collections.Set) and
81+
isinstance(other, collections.Sequence) and
82+
len(self) == len(other) and
83+
tuple(self) == tuple(other)
84+
)
85+
86+
87+
class FrozenOrderedSet(BaseOrderedSet):
88+
'''
89+
A `frozenset` with an order.
90+
91+
This behaves like a `frozenset` (i.e. a set that can't be changed after
92+
creation) except items have an order. (By default they're ordered by
93+
insertion order, but that order can be changed.)
94+
'''
95+
96+
class OrderedSet(BaseOrderedSet, collections.MutableSet):
97+
'''
98+
A `set` with an order.
10699
107-
def __del__(self):
108-
self.clear() # remove circular references
109-
# todo: is this really needed? i'm worried about this making the gc not
110-
# drop circulary-referencing objects.
100+
This behaves like a `set` except items have an order. (By default they're
101+
ordered by insertion order, but that order can be changed.)
102+
'''
111103

112104
def move_to_end(self, key):
113105
'''
@@ -137,5 +129,83 @@ def sort(self, key=None, reverse=False):
137129

138130
self.clear()
139131
self |= sorted_members
132+
133+
def clear(self):
134+
self._end = []
135+
self._end += [None, self._end, self._end]
136+
self._map = {}
140137

141-
138+
139+
def add(self, key):
140+
'''
141+
Add an element to a set.
142+
143+
This has no effect if the element is already present.
144+
'''
145+
if key not in self._map:
146+
end = self._end
147+
curr = end[PREV]
148+
curr[NEXT] = end[PREV] = self._map[key] = [key, curr, end]
149+
150+
def discard(self, key):
151+
"""
152+
Remove an element from a set if it is a member.
153+
154+
If the element is not a member, do nothing.
155+
"""
156+
if key in self._map:
157+
key, prev, next = self._map.pop(key)
158+
prev[NEXT] = next
159+
next[PREV] = prev
160+
161+
def pop(self, last=True):
162+
'''Remove and return an arbitrary set element.'''
163+
if not self:
164+
raise KeyError('set is empty')
165+
key = next(reversed(self) if last else iter(self))
166+
self.discard(key)
167+
return key
168+
169+
170+
class EmittingOrderedSet(OrderedSet):
171+
'''An ordered set that emits to `.emitter` every time it's modified.'''
172+
173+
def __init__(self, emitter, items=()):
174+
if emitter:
175+
from python_toolbox.emitting import Emitter
176+
assert isinstance(emitter, Emitter)
177+
self.emitter = emitter
178+
OrderedSet.__init__(self, items)
179+
180+
181+
def add(self, key):
182+
'''
183+
Add an element to a set.
184+
185+
This has no effect if the element is already present.
186+
'''
187+
if key not in self._map:
188+
end = self._end
189+
curr = end[PREV]
190+
curr[NEXT] = end[PREV] = self._map[key] = [key, curr, end]
191+
if self.emitter:
192+
self.emitter.emit()
193+
194+
195+
def discard(self, key):
196+
'''
197+
Remove an element from a set if it is a member.
198+
199+
If the element is not a member, do nothing.
200+
'''
201+
if key in self._map:
202+
key, prev, next = self._map.pop(key)
203+
prev[NEXT] = next
204+
next[PREV] = prev
205+
if self.emitter:
206+
self.emitter.emit()
207+
208+
209+
def set_emitter(self, emitter):
210+
'''Set `emitter` to be emitted with on every modification.'''
211+
self.emitter = emitter

source_py3/test_python_toolbox/test_nifty_collections/test_ordered_set/test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def test():
3030
def test_sort():
3131

3232
ordered_set = OrderedSet([5, 61, 2, 7, 2])
33-
assert ordered_set == {5, 61, 2, 7}
33+
assert ordered_set != {5, 61, 2, 7}
3434
ordered_set.move_to_end(61)
3535
assert list(ordered_set) == [5, 2, 7, 61]
3636
ordered_set.sort()

0 commit comments

Comments
 (0)