Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9f87293

Browse files
committed
Ouch. The test suite *really* needs work!!!!! There were several
superficial errors and one deep one that aren't currently caught. I'm headed for bed after this checkin. - Fixed several typos introduced by Raymond Hettinger (through cut-n-paste from my template): it's _as_temporarily_immutable, not _as_temporary_immutable, and moreover when the element is added, we should use _as_immutable. - Made the seq argument to ImmutableSet.__init__ optional, so we can write ImmutableSet() to create an immutable empty set. - Rename the seq argument to Set and ImmutableSet to iterable. - Add a Set.__hash__ method that raises a TypeError. We inherit a default __hash__ implementation from object, and we don't want that. We can then catch this in update(), so that e.g. s.update([Set([1])]) will transform the Set([1]) to ImmutableSet([1]). - Added the dance to catch TypeError and try _as_immutable in the constructors too (by calling _update()). This is needed so that Set([Set([1])]) is correctly interpreted as Set([ImmutableSet([1])]). (I was puzzled by a side effect of this and the inherited __hash__ when comparing two sets of sets while testing different powerset implementations: the Set element passed to a Set constructor wasn't transformed to an ImmutableSet, and then the dictionary didn't believe the Set found in one dict it was the same as ImmutableSet in the other, because the hashes were different.) - Refactored Set.update() and both __init__() methods; moved the body of update() into BaseSet as _update(), and call this from __init__() and update(). - Changed the NotImplementedError in BaseSet.__init__ to TypeError, both for consistency with basestring() and because we have to use TypeError when denying Set.__hash__. Together those provide sufficient evidence that an unimplemented method needs to raise TypeError.
1 parent 2658822 commit 9f87293

1 file changed

Lines changed: 38 additions & 46 deletions

File tree

Lib/sets.py

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
# - Guido van Rossum rewrote much of the code, made some API changes,
5353
# and cleaned up the docstrings.
5454
#
55-
# - Raymond Hettinger implemented a number of speedups and other
56-
# improvements.
55+
# - Raymond Hettinger added a number of speedups and other
56+
# bugs^H^H^H^Himprovements.
5757

5858

5959
__all__ = ['BaseSet', 'Set', 'ImmutableSet']
@@ -70,9 +70,8 @@ def __init__(self):
7070
"""This is an abstract class."""
7171
# Don't call this from a concrete subclass!
7272
if self.__class__ is BaseSet:
73-
# XXX Maybe raise TypeError instead, like basestring()?
74-
raise NotImplementedError, ("BaseSet is an abstract class. "
75-
"Use Set or ImmutableSet.")
73+
raise TypeError, ("BaseSet is an abstract class. "
74+
"Use Set or ImmutableSet.")
7675

7776
# Standard protocols: __len__, __repr__, __str__, __iter__
7877

@@ -233,7 +232,7 @@ def __contains__(self, element):
233232
try:
234233
return element in self._data
235234
except TypeError:
236-
transform = getattr(element, "_as_temporary_immutable", None)
235+
transform = getattr(element, "_as_temporarily_immutable", None)
237236
if transform is None:
238237
raise # re-raise the TypeError exception we caught
239238
return transform() in self._data
@@ -279,6 +278,21 @@ def _compute_hash(self):
279278
result ^= hash(elt)
280279
return result
281280

281+
def _update(self, iterable):
282+
# The main loop for update() and the subclass __init__() methods.
283+
# XXX This can be optimized a bit by first trying the loop
284+
# without setting up a try/except for each element.
285+
data = self._data
286+
value = True
287+
for element in iterable:
288+
try:
289+
data[element] = value
290+
except TypeError:
291+
transform = getattr(element, "_as_immutable", None)
292+
if transform is None:
293+
raise # re-raise the TypeError exception we caught
294+
data[transform()] = value
295+
282296

283297
class ImmutableSet(BaseSet):
284298
"""Immutable set class."""
@@ -287,26 +301,12 @@ class ImmutableSet(BaseSet):
287301

288302
# BaseSet + hashing
289303

290-
def __init__(self, seq):
291-
"""Construct an immutable set from a sequence."""
292-
# XXX Maybe this should default seq to None?
293-
# XXX Creating an empty immutable set is not unheard of.
304+
def __init__(self, iterable=None):
305+
"""Construct an immutable set from an optional iterable."""
294306
self._hashcode = None
295-
self._data = data = {}
296-
# I don't know a faster way to do this in pure Python.
297-
# Custom code written in C only did it 65% faster,
298-
# preallocating the dict to len(seq); without
299-
# preallocation it was only 25% faster. So the speed of
300-
# this Python code is respectable. Just copying True into
301-
# a local variable is responsible for a 7-8% speedup.
302-
value = True
303-
# XXX Should this perhaps look for _as_immutable?
304-
# XXX If so, should use self.update(seq).
305-
# XXX (Well, ImmutableSet doesn't have update(); the base
306-
# XXX class could have _update() which does this though, and
307-
# XXX we could use that here and in Set.update().)
308-
for key in seq:
309-
data[key] = value
307+
self._data = {}
308+
if iterable is not None:
309+
self._update(iterable)
310310

311311
def __hash__(self):
312312
if self._hashcode is None:
@@ -321,15 +321,16 @@ class Set(BaseSet):
321321

322322
# BaseSet + operations requiring mutability; no hashing
323323

324-
def __init__(self, seq=None):
325-
"""Construct an immutable set from a sequence."""
326-
self._data = data = {}
327-
if seq is not None:
328-
value = True
329-
# XXX Should this perhaps look for _as_immutable?
330-
# XXX If so, should use self.update(seq).
331-
for key in seq:
332-
data[key] = value
324+
def __init__(self, iterable=None):
325+
"""Construct a set from an optional iterable."""
326+
self._data = {}
327+
if iterable is not None:
328+
self._update(iterable)
329+
330+
def __hash__(self):
331+
"""A Set cannot be hashed."""
332+
# We inherit object.__hash__, so we must deny this explicitly
333+
raise TypeError, "Can't hash a Set, only an ImmutableSet."
333334

334335
# In-place union, intersection, differences
335336

@@ -380,16 +381,7 @@ def difference_update(self, other):
380381

381382
def update(self, iterable):
382383
"""Add all values from an iterable (such as a list or file)."""
383-
data = self._data
384-
value = True
385-
for element in iterable:
386-
try:
387-
data[element] = value
388-
except TypeError:
389-
transform = getattr(element, "_as_temporary_immutable", None)
390-
if transform is None:
391-
raise # re-raise the TypeError exception we caught
392-
data[transform()] = value
384+
self._update(iterable)
393385

394386
def clear(self):
395387
"""Remove all elements from this set."""
@@ -405,7 +397,7 @@ def add(self, element):
405397
try:
406398
self._data[element] = True
407399
except TypeError:
408-
transform = getattr(element, "_as_temporary_immutable", None)
400+
transform = getattr(element, "_as_immutable", None)
409401
if transform is None:
410402
raise # re-raise the TypeError exception we caught
411403
self._data[transform()] = True
@@ -418,7 +410,7 @@ def remove(self, element):
418410
try:
419411
del self._data[element]
420412
except TypeError:
421-
transform = getattr(element, "_as_temporary_immutable", None)
413+
transform = getattr(element, "_as_temporarily_immutable", None)
422414
if transform is None:
423415
raise # re-raise the TypeError exception we caught
424416
del self._data[transform()]

0 commit comments

Comments
 (0)