xrange not hashable - why not?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Gerrit Holl

    xrange not hashable - why not?

    Hi,

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

    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

    Because:
    - I found it easier to extend than:
    if 0 <= n < 4: return "few"
    elif ... # etc
    - And better readable than:
    if k[0] <= n < k[1]: # using tuples
    - And much more memory efficient than tuple(...) (especially the
    last one ;-)

    It would not be difficult to let xrange have a hash:

    hash((self.star t, self.step, self.stop))

    would be sufficient, I think.

    Hmm, start, step and stop appear to have disappeared in Python 2.3...
    certainly makes it somewhat more difficult. I shouldn't even to containment
    testing according to PEP 260, and Python 2.3.3 should warn me not to
    do... it doesn't, however.

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

    yours,
    Gerrit.

  • Sean Ross

    #2
    Re: xrange not hashable - why not?


    "Gerrit Holl" <gerrit@nl.linu x.org> wrote in message
    news:mailman.76 4.1075042048.12 720.python-list@python.org ...[color=blue]
    > Hi,
    >
    > why is an xrange object not hashable?[/color]


    I don't know the answer for this.

    [color=blue]
    > I was trying to do something like:
    >
    > 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[/color]

    There's a bit of an efficiency issue with using 'n in k' for something like
    xrange(100, sys.maxint),
    since you have to walk through the items in that range to see if n is in
    there, and that's a large range.
    Perhaps this would help:

    class bounds:
    def __init__(self, start, stop, step=1):
    self.start = start
    self.stop = stop
    self.step = step
    def is_step(self, other):
    q, r = divmod(other-self.start, self.step)
    return r == 0
    def __contains__(se lf, other):
    return self.start <= other < self.stop and self.is_step(ot her)
    def __hash__(self):
    return id(self)
    def __repr__(self):
    return "bounds(start=% s, stop=%s, step=%s)"%\
    (self.start, self.stop, self.step)

    import sys
    import random

    comments = {
    bounds(0, 4): "Few",
    bounds(4, 10): "Several",
    bounds(10, 100): "A lot",
    bounds(100, sys.maxint): "Can't count them"}

    n = random.randint( 0, sys.maxint)
    print n

    for k, v in comments.iterit ems():
    print k
    if n in k:
    print v
    break
    else:
    print n, "out of bounds"

    HTH,
    Sean


    Comment

    • Sean Ross

      #3
      Re: xrange not hashable - why not?


      "Sean Ross" <sross@connectm ail.carleton.ca > wrote in message
      news:FSRQb.95$q U3.31981@news20 .bellglobal.com ...[color=blue]
      > There's a bit of an efficiency issue with using 'n in k' for something[/color]
      like[color=blue]
      > xrange(100, sys.maxint),
      > since you have to walk through the items in that range to see if n is in
      > there, and that's a large range.[/color]

      Hmm, I don't think what I said there is correct, please disregard.

      Sorry for any confusion,
      Sean


      Comment

      • Irmen de Jong

        #4
        Re: xrange not hashable - why not?

        Sean Ross wrote:
        [color=blue][color=green]
        >>xrange(100, sys.maxint),
        >>since you have to walk through the items in that range to see if n is in
        >>there, and that's a large range.[/color]
        >
        >
        > Hmm, I don't think what I said there is correct, please disregard.[/color]


        I think you're right though:

        90000000 in xrange(1,sys.ma xint) takes a long time to complete....

        --Irmen

        Comment

        • Martin v. Löwis

          #5
          Re: xrange not hashable - why not?

          Gerrit Holl wrote:[color=blue]
          > why is an xrange object not hashable?[/color]

          Because they don't have a notion of equality
          beyond identity.

          Regards,
          Martin

          Comment

          • Peter Otten

            #6
            Re: xrange not hashable - why not?

            Gerrit Holl wrote:
            [color=blue]
            > I was trying to do something like:
            >
            > 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[/color]

            [...]
            [color=blue]
            > So, should I use one of the alternatives after all? Or does someone have
            > something better to offer?[/color]

            I think you want bisect():

            import bisect

            ranges = [
            (0, "Few"),
            (4, "Several"),
            (10, "A lot"),
            (100, "Can't count them")
            ]

            def makelookup(r):
            bounds = [i[0] for i in r][1:]
            names = [i[1] for i in r]
            def find(value):
            return names[bisect.bisect(b ounds, value)]
            return find

            lookup = makelookup(rang es)

            # use it
            last = ""
            for i in range(-100, 1000):
            if last != lookup(i):
            last = lookup(i)
            print i, last

            Note that names needs one more entry than bounds. I "fixed" that by
            discarding the first bounds item.

            Peter

            Comment

            • Josiah Carlson

              #7
              Re: xrange not hashable - why not?

              > why is an xrange object not hashable?[color=blue]
              > I was trying to do something like:[/color]

              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.
              [color=blue]
              > 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[/color]
              [color=blue]
              > It would not be difficult to let xrange have a hash:
              >
              > hash((self.star t, self.step, self.stop))[/color]

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

              [color=blue]
              > Hmm, start, step and stop appear to have disappeared in Python 2.3...[/color]

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

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

              [color=blue]
              > So, should I use one of the alternatives after all? Or does someone have
              > something better to offer?[/color]

              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.star t, self.stop, self.step))
              def __cmp__(self, other):
              if isinstance(othe r, 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(sel f.start, self.stop, self.step))
              def __contains__(se lf, 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

              Comment

              Working...