1 """Classes to represent arbitrary sets (including sets of sets).
2
3 This module implements sets using dictionaries whose values are
4 ignored. The usual operations (union, intersection, deletion, etc.)
5 are provided as both methods and operators.
6
7 Important: sets are not sequences! While they support 'x in s',
8 'len(s)', and 'for x in s', none of those operations are unique for
9 sequences; for example, mappings support all three as well. The
10 characteristic operation for sequences is subscripting with small
11 integers: s[i], for i in range(len(s)). Sets don't support
12 subscripting at all. Also, sequences allow multiple occurrences and
13 their elements have a definite order; sets on the other hand don't
14 record multiple occurrences and don't remember the order of element
15 insertion (which is why they don't support s[i]).
16
17 The following classes are provided:
18
19 BaseSet -- All the operations common to both mutable and immutable
20 sets. This is an abstract class, not meant to be directly
21 instantiated.
22
23 Set -- Mutable sets, subclass of BaseSet; not hashable.
24
25 ImmutableSet -- Immutable sets, subclass of BaseSet; hashable.
26 An iterable argument is mandatory to create an ImmutableSet.
27
28 _TemporarilyImmutableSet -- A wrapper around a Set, hashable,
29 giving the same hash value as the immutable set equivalent
30 would have. Do not use this class directly.
31
32 Only hashable objects can be added to a Set. In particular, you cannot
33 really add a Set as an element to another Set; if you try, what is
34 actually added is an ImmutableSet built from it (it compares equal to
35 the one you tried adding).
36
37 When you ask if `x in y' where x is a Set and y is a Set or
38 ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and
39 what's tested is actually `z in y'.
40
41 """
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 exec('from itertools import ifilterfalse as filterfalse')
59
60 __all__ = ['BaseSet', 'Set', 'ImmutableSet']
61
63 """Common base class for mutable and immutable sets."""
64
65 __slots__ = ['_data']
66
67
68
70 """This is an abstract class."""
71
72 if self.__class__ is BaseSet:
73 raise TypeError("BaseSet is an abstract class. "
74 "Use Set or ImmutableSet.")
75
76
77
79 """Return the number of elements of a set."""
80 return len(self._data)
81
83 """Return string representation of a set.
84
85 This looks like 'Set([<list of elements>])'.
86 """
87 return self._repr()
88
89
90 __str__ = __repr__
91
92 - def _repr(self, sort_them=False):
93 elements = list(self._data.keys())
94 if sort_them:
95 elements.sort()
96 return '%s(%r)' % (self.__class__.__name__, elements)
97
99 """Return an iterator over the elements or a set.
100
101 This is the keys iterator for the underlying dict.
102 """
103
104 return (self._data.iterkeys)()
105
106
107
108
109
110
112 raise TypeError("can't compare sets using cmp()")
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
130 if isinstance(other, BaseSet):
131 return self._data == other._data
132 else:
133 return False
134
136 if isinstance(other, BaseSet):
137 return self._data != other._data
138 else:
139 return True
140
141
142
148
149 __copy__ = copy
150
152 """Return a deep copy of a set; used by copy module."""
153
154
155
156
157
158 from copy import deepcopy
159 result = self.__class__()
160 memo[id(self)] = result
161 data = result._data
162 value = True
163 for elt in self:
164 data[deepcopy(elt, memo)] = value
165 return result
166
167
168
169
170
171
172
173
174
175
177 """Return the union of two sets as a new set.
178
179 (I.e. all elements that are in either set.)
180 """
181 if not isinstance(other, BaseSet):
182 return NotImplemented
183 return self.union(other)
184
186 """Return the union of two sets as a new set.
187
188 (I.e. all elements that are in either set.)
189 """
190 result = self.__class__(self)
191 result._update(other)
192 return result
193
195 """Return the intersection of two sets as a new set.
196
197 (I.e. all elements that are in both sets.)
198 """
199 if not isinstance(other, BaseSet):
200 return NotImplemented
201 return self.intersection(other)
202
204 """Return the intersection of two sets as a new set.
205
206 (I.e. all elements that are in both sets.)
207 """
208 if not isinstance(other, BaseSet):
209 other = Set(other)
210 if len(self) <= len(other):
211 little, big = self, other
212 else:
213 little, big = other, self
214 common = iter(filter(big._data.has_key, little))
215 return self.__class__(common)
216
218 """Return the symmetric difference of two sets as a new set.
219
220 (I.e. all elements that are in exactly one of the sets.)
221 """
222 if not isinstance(other, BaseSet):
223 return NotImplemented
224 return self.symmetric_difference(other)
225
227 """Return the symmetric difference of two sets as a new set.
228
229 (I.e. all elements that are in exactly one of the sets.)
230 """
231 result = self.__class__()
232 data = result._data
233 value = True
234 selfdata = self._data
235 try:
236 otherdata = other._data
237 except AttributeError:
238 otherdata = Set(other)._data
239 for elt in filterfalse(otherdata.has_key, selfdata):
240 data[elt] = value
241 for elt in filterfalse(selfdata.has_key, otherdata):
242 data[elt] = value
243 return result
244
246 """Return the difference of two sets as a new Set.
247
248 (I.e. all elements that are in this set and not in the other.)
249 """
250 if not isinstance(other, BaseSet):
251 return NotImplemented
252 return self.difference(other)
253
255 """Return the difference of two sets as a new Set.
256
257 (I.e. all elements that are in this set and not in the other.)
258 """
259 result = self.__class__()
260 data = result._data
261 try:
262 otherdata = other._data
263 except AttributeError:
264 otherdata = Set(other)._data
265 value = True
266 for elt in filterfalse(otherdata.has_key, self):
267 data[elt] = value
268 return result
269
270
271
273 """Report whether an element is a member of a set.
274
275 (Called in response to the expression `element in self'.)
276 """
277 try:
278 return element in self._data
279 except TypeError:
280 transform = getattr(element, "__as_temporarily_immutable__", None)
281 if transform is None:
282 raise
283 return transform() in self._data
284
285
286
288 """Report whether another set contains this set."""
289 self._binary_sanity_check(other)
290 if len(self) > len(other):
291 return False
292 for elt in filterfalse(other._data.has_key, self):
293 return False
294 return True
295
297 """Report whether this set contains another set."""
298 self._binary_sanity_check(other)
299 if len(self) < len(other):
300 return False
301 for elt in filterfalse(self._data.has_key, other):
302 return False
303 return True
304
305
306 __le__ = issubset
307 __ge__ = issuperset
308
312
316
317
318
320
321
322 if not isinstance(other, BaseSet):
323 raise TypeError("Binary operation only permitted between sets")
324
326
327
328
329
330
331 result = 0
332 for elt in self:
333 result ^= hash(elt)
334 return result
335
337
338 data = self._data
339
340
341 if isinstance(iterable, BaseSet):
342 data.update(iterable._data)
343 return
344
345 value = True
346
347 if type(iterable) in (list, tuple, xrange):
348
349
350 it = iter(iterable)
351 while True:
352 try:
353 for element in it:
354 data[element] = value
355 return
356 except TypeError:
357 transform = getattr(element, "__as_immutable__", None)
358 if transform is None:
359 raise
360 data[transform()] = value
361 else:
362
363 for element in iterable:
364 try:
365 data[element] = value
366 except TypeError:
367 transform = getattr(element, "__as_immutable__", None)
368 if transform is None:
369 raise
370 data[transform()] = value
371
372
374 """Immutable set class."""
375
376 __slots__ = ['_hashcode']
377
378
379
381 """Construct an immutable set from an optional iterable."""
382 self._hashcode = None
383 self._data = {}
384 if iterable is not None:
385 self._update(iterable)
386
391
394
397
399 """ Mutable set class."""
400
401 __slots__ = []
402
403
404
406 """Construct a set from an optional iterable."""
407 self._data = {}
408 if iterable is not None:
409 self._update(iterable)
410
412
413 return self._data,
414
417
419 """A Set cannot be hashed."""
420
421 raise TypeError("Can't hash a Set, only an ImmutableSet.")
422
423
424
425
426
427
433
435 """Update a set with the union of itself and another."""
436 self._update(other)
437
439 """Update a set with the intersection of itself and another."""
440 self._binary_sanity_check(other)
441 self._data = (self & other)._data
442 return self
443
445 """Update a set with the intersection of itself and another."""
446 if isinstance(other, BaseSet):
447 self &= other
448 else:
449 self._data = (self.intersection(other))._data
450
456
458 """Update a set with the symmetric difference of itself and another."""
459 data = self._data
460 value = True
461 if not isinstance(other, BaseSet):
462 other = Set(other)
463 if self is other:
464 self.clear()
465 for elt in other:
466 if elt in data:
467 del data[elt]
468 else:
469 data[elt] = value
470
476
478 """Remove all elements of another set from this set."""
479 data = self._data
480 if not isinstance(other, BaseSet):
481 other = Set(other)
482 if self is other:
483 self.clear()
484 for elt in filter(data.has_key, other):
485 del data[elt]
486
487
488
490 """Add all values from an iterable (such as a list or file)."""
491 self._update(iterable)
492
494 """Remove all elements from this set."""
495 self._data.clear()
496
497
498
499 - def add(self, element):
500 """Add an element to a set.
501
502 This has no effect if the element is already present.
503 """
504 try:
505 self._data[element] = True
506 except TypeError:
507 transform = getattr(element, "__as_immutable__", None)
508 if transform is None:
509 raise
510 self._data[transform()] = True
511
513 """Remove an element from a set; it must be a member.
514
515 If the element is not a member, raise a KeyError.
516 """
517 try:
518 del self._data[element]
519 except TypeError:
520 transform = getattr(element, "__as_temporarily_immutable__", None)
521 if transform is None:
522 raise
523 del self._data[transform()]
524
526 """Remove an element from a set if it is a member.
527
528 If the element is not a member, do nothing.
529 """
530 try:
531 self.remove(element)
532 except KeyError:
533 pass
534
536 """Remove and return an arbitrary set element."""
537 return self._data.popitem()[0]
538
542
546
547
558
559
560
561
562
563
564