I have a generator and I would like to know if I can use it without having to worry about StopIteration , and I would like to use it without the for item in generator
. I would like to use it with a while statement for example ( or other constructs ). How could I do that ?
views:
273answers:
3
+13
A:
built-in function
next(iterator[, default])
Retrieve the next item from the iterator by calling its__next__()
method. If default is given, it is returned if the iterator is exhausted, otherwise StopIteration is raised.
In Python 2.5 and older:
raiseStopIteration = object()
def next(iterator, default=raiseStopIteration):
if not hasattr(iterator, 'next'):
raise TypeError("not an iterator")
try:
return iterator.next()
except StopIteration:
if default is raiseStopIteration:
raise
else:
return default
SilentGhost
2009-02-01 11:12:53
I've added `next()` implementation for Python 2.5
J.F. Sebastian
2009-02-01 12:01:16
This is a better solution than the accepted one.
Carl Meyer
2009-02-01 19:20:40
A:
Use this to wrap your generator:
class GeneratorWrap(object):
def __init__(self, generator):
self.generator = generator
def __iter__(self):
return self
def next(self):
for o in self.generator:
return o
raise StopIteration # If you don't care about the iterator protocol, remove this line and the __iter__ method.
Use it like this:
def example_generator():
for i in [1,2,3,4,5]:
yield i
gen = GeneratorWrap(example_generator())
print gen.next() # prints 1
print gen.next() # prints 2
Update: Please use the answer below because it is much better than this one.
Evan Fosmark
2009-02-01 11:13:09
@Tsunami, I should also mention that this behavior can be changed by adding another return statement below the for loop in the next() method. Whatever you have it return will be the default after the generator has exhausted.
Evan Fosmark
2009-02-01 11:19:14
By the way,can you please explain what's the logic behind returning self in __iter__ ?
Geo
2009-02-01 11:24:08
An iterator `next()` method must raise StopIteration. Your `__iter__()` method returns `self` but the object breaks the contract for iterator object.
J.F. Sebastian
2009-02-01 11:25:27
@Tsunami, no problem. Basically, it isn't too useful in this example, but it allows for it to be used in a "for x in y" sort of loop if it were needed to. You could remove it if you are 100% sure that you'll never use it that way.
Evan Fosmark
2009-02-01 11:25:50
I understand that not raising the exception breaks the contract , but adding the line brings us back to square 1 :) . Anyway , thanks for your response and your helpful comments !
Geo
2009-02-01 11:31:22
If you don't want an iterator, don't use it. In this case don't use `next` name for the method if your object is not an iterator. Call it something else e.g. `safe_next(self, sentinel=None)` method -- return `sentinel` instead of throwing StopIteration. builtin `next()` function is different.
J.F. Sebastian
2009-02-01 11:57:54
A better name would be `next_noraise()` for the method. Even better just use @SilentGhost's suggestion.
J.F. Sebastian
2009-02-01 12:31:00
+1
A:
Another options is to read all generator values at once:
>>> alist = list(agenerator)
Example:
>>> def f():
... yield 'a'
...
>>> a = list(f())
>>> a[0]
'a'
>>> len(a)
1
J.F. Sebastian
2009-02-01 12:16:35
@hasen: list[index] doesn't raise StopIteration. It can be used without `for` loop. It can be used with a `while` loop. All conditions from the question are satisfied.
J.F. Sebastian
2009-02-01 12:26:04
As soon as @Torsten Marek get StopIteration from itertools.count() I'll read all values. :) OP asks only about generators that can produce StopIteration, so infinite generators just do not apply.
J.F. Sebastian
2009-02-01 23:18:57