A generator expression is an expression that returns a generator object. Basically, a generator function is a function that contains a yield statement and returns a generator object.
What Is Yield In Python? The Yield keyword in Python is similar to a return statement used for returning values or objects in Python. However, there is a slight difference. The yield statement returns a generator object to the one who calls the function which contains yield, instead of simply returning a value.
Python provides a generator to create your own iterator function. A generator is a special type of function which does not return a single value, instead, it returns an iterator object with a sequence of values. In a generator function, a yield statement is used rather than a return statement.
Suggestion:
def peek(iterable):
try:
first = next(iterable)
except StopIteration:
return None
return first, itertools.chain([first], iterable)
Usage:
res = peek(mysequence)
if res is None:
# sequence is empty. Do stuff.
else:
first, mysequence = res
# Do something with first, maybe?
# Then iterate over the sequence:
for element in mysequence:
# etc.
The simple answer to your question: no, there is no simple way. There are a whole lot of work-arounds.
There really shouldn't be a simple way, because of what generators are: a way to output a sequence of values without holding the sequence in memory. So there's no backward traversal.
You could write a has_next function or maybe even slap it on to a generator as a method with a fancy decorator if you wanted to.
A simple way is to use the optional parameter for next() which is used if the generator is exhausted (or empty). For example:
iterable = some_generator()
_exhausted = object()
if next(iterable, _exhausted) is _exhausted:
print('generator is empty')
next(generator, None) is not None
Or replace None
but whatever value you know it's not in your generator.
Edit: Yes, this will skip 1 item in the generator. Often, however, I check whether a generator is empty only for validation purposes, then don't really use it. Or otherwise I do something like:
def foo(self):
if next(self.my_generator(), None) is None:
raise Exception("Not initiated")
for x in self.my_generator():
...
That is, this works if your generator comes from a function, as in generator()
.
The best approach, IMHO, would be to avoid a special test. Most times, use of a generator is the test:
thing_generated = False
# Nothing is lost here. if nothing is generated,
# the for block is not executed. Often, that's the only check
# you need to do. This can be done in the course of doing
# the work you wanted to do anyway on the generated output.
for thing in my_generator():
thing_generated = True
do_work(thing)
If that's not good enough, you can still perform an explicit test. At this point, thing
will contain the last value generated. If nothing was generated, it will be undefined - unless you've already defined the variable. You could check the value of thing
, but that's a bit unreliable. Instead, just set a flag within the block and check it afterward:
if not thing_generated:
print "Avast, ye scurvy dog!"
I hate to offer a second solution, especially one that I would not use myself, but, if you absolutely had to do this and to not consume the generator, as in other answers:
def do_something_with_item(item):
print item
empty_marker = object()
try:
first_item = my_generator.next()
except StopIteration:
print 'The generator was empty'
first_item = empty_marker
if first_item is not empty_marker:
do_something_with_item(first_item)
for item in my_generator:
do_something_with_item(item)
Now I really don't like this solution, because I believe that this is not how generators are to be used.
Just fell on this thread and realized that a very simple and easy to read answer was missing:
def is_empty(generator):
for item in generator:
return False
return True
If we are not suppose to consume any item then we need to re-inject the first item into the generator:
def is_empty_no_side_effects(generator):
try:
item = next(generator)
def my_generator():
yield item
yield from generator
return my_generator(), False
except StopIteration:
return (_ for _ in []), True
Example:
>>> g=(i for i in [])
>>> g,empty=is_empty_no_side_effects(g)
>>> empty
True
>>> g=(i for i in range(10))
>>> g,empty=is_empty_no_side_effects(g)
>>> empty
False
>>> list(g)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With