tags:

views:

135

answers:

3

Python 2.6+ and 3.* have next(), but pre-2.6 only offers the object.next method. Is there a way to get the next() style in pre-2.6; some "def next():" construction perhaps?

+10  A: 
class Throw(object): pass
throw = Throw() # easy sentinel hack
def next(iterator, default=throw):
  """next(iterator[, default])

  Return the next item from the iterator. If default is given
  and the iterator is exhausted, it is returned instead of
  raising StopIteration.
  """
  try:
    iternext = iterator.next.__call__
    # this way an AttributeError while executing next() isn't hidden
    # (2.6 does this too)
  except AttributeError:
    raise TypeError("%s object is not an iterator" % type(iterator).__name__)
  try:
    return iternext()
  except StopIteration:
    if default is throw:
      raise
    return default

(throw = object() works too, but this generates better docs when inspecting, e.g. help(next). None is not suitable, because you must treat next(it) and next(it, None) differently.)

Roger Pate
@pantsgolem pointed out that in Python 3, `next()` raises `TypeError` when you call it on a non-iterator. I edited this answer to catch `AttributeError` and re-raise it as `TypeError`. (I hope this is good StackOverflow etiquette; this is the first time I have edited someone else's answer!)
steveha
More importantly, next() in 2.6 raises TypeError in that case. Editing to include it is fine by me, though I modified the raise.
Roger Pate
+5  A: 

R. Pate seems to have a good answer. One extra bell to add: if you're writing code to run on many different versions of Python, you can conditionalize the definition:

try:
    next = next
except NameError:
    def next():
        # blah blah etc

That way you have next defined in any case, but you're using the built in implementation where it's available.

I use next = next so that I can put this definition in a module, then elsewhere in my code use:

from backward import next
Ned Batchelder
Can't the test just be next? E.g. `try: next; except ...`
Roger Pate
It has to do with the way I include these definitions in my project. I've udpated the answer.
Ned Batchelder
+2  A: 

Simpler method:

import operator

next = operator.methodcaller("next")

Ned's suggestion about putting it in a try block works here as well, but if you're going to go that route, one minor note: in Python 3, calling next() on a non-iterator raises a TypeError, whereas this version would raise an AttributeError instead.

Edit: Never mind. As steveha points out, operator.methodcaller() was only introduced in 2.6, which is a shame.

pantsgolem
I have edited R.Pate's answer to raise `AttributeError` when calling `next()` on an object that does not have a `.next()` method.
steveha
`operator.methodcaller()` looks interesting, but it was added in Python 2.6. R.Pate's answer should work in any Python with iterators.
steveha