xrange not hashable - why not?

Josiah Carlson jcarlson at nospam.uci.edu
Sun Jan 25 19:42:19 CET 2004

> why is an xrange object not hashable?
> I was trying to do something like:

Could be a hold-over from xrange trying to have all of the features of a 
list returned by range; lists are unhashable because they are mutable.

> comments = {
>     xrange(0, 4): "Few",
>     xrange(4, 10): "Several",
>     xrange(10, 100): "A lot",
>     xrange(100, sys.maxint): "Can't count them"}
> for (k, v) in comments.items():
>     if n in k:
>         commentaar = v
>         break

> It would not be difficult to let xrange have a hash:
>     hash((self.start, self.step, self.stop))

I don't believe anyone ever said it was. *wink*

> Hmm, start, step and stop appear to have disappeared in Python 2.3...

I don't know about that:
Python 2.3.2 (#49, Oct  2 2003, 20:02:00) [MSC v.1200 32 bit (Intel)] on 
Type "help", "copyright", "credits" or "license" for more information.
 >>> help(xrange)
Help on class xrange in module __builtin__:

class xrange(object)
  |  xrange([start,] stop[, step]) -> xrange object

> So, should I use one of the alternatives after all? Or does someone have
> something better to offer?

Honestly it is one of those things that are easily remedied by a 
custom-built class.  Like the below:

class HashableXRange:
     def __init__(self, s1, s2=None, s3=None):
         if s2 is None:
             s1, s2, s3 = 0, s1, 1
         elif s3 is None:
             s3 = 1
         self.start = s1
         self.stop = s2
         self.step = s3
     def __hash__(self):
         return hash((self.start, self.stop, self.step))
     def __cmp__(self, other):
         if isinstance(other, self.__class__):
             return cmp(self.start, other.start) or\
                    cmp(self.stop, other.stop) or\
                    -cmp(self.step, other.step)
         return cmp(self.start, other)
     def __iter__(self):
         return iter(xrange(self.start, self.stop, self.step))
     def __contains__(self, object):
         if self.start <= object < self.stop:
             if (object-self.start) % self.step == 0:
                 return 1
         return 0

I included the __cmp__ function in order to give it a richer set of 
behavior.  One thing to note about hashes is that they are not required 
to be unique.  As such, the hashing algorithm is not the absolute best 
algorithm ever.  It is pretty good, but it is not guaranteed that 
hash((a,b,c)) != hash((d,e,f)) for different sets of three objects. 
Better to be safe than sorry.

I would have subclassed xrange, but Python 2.3 doesn't like that. 
Apparently there is no subclassing for xranges.

  - Josiah

More information about the Python-list mailing list