How to Know If a Generator Is Empty from the Start

How do I know if a generator is empty from the start?

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.

python best way to check if generator is empty

Call next(<generator>, None) on it and check for the result not being None, or use your custom value if you expect None as a part of the generator output.

Alternatively, don't, but catch the StopIteration:

try:
next(<generator>)
except StopIteration:
print("I'm empty")

python generator with check for empty condition

I would not usually implement this kind
of generator. There is an idiomatic way how to test if a iterator it
is exhausted:

try:
next_item = next(it)
except StopIteration:
# exhausted, handle this case

Substituting this EAFP idiom by some project-specific LBYL idiom seems
confusing and not beneficial at all.

That said, here is how I would implement this if I really wanted to:

class MyIterator(object):
def __init__(self, iterable):
self._iterable = iter(iterable)
self._exhausted = False
self._cache_next_item()
def _cache_next_item(self):
try:
self._next_item = next(self._iterable)
except StopIteration:
self._exhausted = True
def __iter__(self):
return self
def next(self):
if self._exhausted:
raise StopIteration
next_item = self._next_item
self._cache_next_item()
return next_item
def __nonzero__(self):
return not self._exhausted

Detect if generator function is empty, otherwise iterate over it

You can't tell directly whether a generator is empty. This is by design. A key principle behind generators is they do not hold in memory all items of a generated sequence.

But you can do something like this:

from itertools import chain

def check_first_and_iterate(iterable):
try:
first = next(iterable)
for item in chain([first], item):
bar(item)
except StopIteration:
foo()

check_first_and_iterate(iterable)

How to define an empty generator function?

You can use return once in a generator; it stops iteration without yielding anything, and thus provides an explicit alternative to letting the function run out of scope. So use yield to turn the function into a generator, but precede it with return to terminate the generator before yielding anything.

>>> def f():
... return
... yield
...
>>> list(f())
[]

I'm not sure it's that much better than what you have -- it just replaces a no-op if statement with a no-op yield statement. But it is more idiomatic. Note that just using yield doesn't work.

>>> def f():
... yield
...
>>> list(f())
[None]

Why not just use iter(())?

This question asks specifically about an empty generator function. For that reason, I take it to be a question about the internal consistency of Python's syntax, rather than a question about the best way to create an empty iterator in general.

If question is actually about the best way to create an empty iterator, then you might agree with Zectbumo about using iter(()) instead. However, it's important to observe that iter(()) doesn't return a function! It directly returns an empty iterable. Suppose you're working with an API that expects a callable that returns an iterable each time it's called, just like an ordinary generator function. You'll have to do something like this:

def empty():
return iter(())

(Credit should go to Unutbu for giving the first correct version of this answer.)

Now, you may find the above clearer, but I can imagine situations in which it would be less clear. Consider this example of a long list of (contrived) generator function definitions:

def zeros():
while True:
yield 0

def ones():
while True:
yield 1

...

At the end of that long list, I'd rather see something with a yield in it, like this:

def empty():
return
yield

or, in Python 3.3 and above (as suggested by DSM), this:

def empty():
yield from ()

The presence of the yield keyword makes it clear at the briefest glance that this is just another generator function, exactly like all the others. It takes a bit more time to see that the iter(()) version is doing the same thing.

It's a subtle difference, but I honestly think the yield-based functions are more readable and maintainable.

See also this great answer from user3840170 that uses dis to show another reason why this approach is preferable: it emits the fewest instructions when compiled.

Pythonic way to react on empty generators?

This removes the need for a flag in addition to avoid the useless for loop. You can also adapt it as a decorator to make it possible to forward the call arguments and still be reusable

def check_if_empty_first(gen):
it = gen() # This is optional, depends if you want to make it reusable, and it you want to call with check_if_empty_first(gen) or check_if_empty_first(gen())
try:
yield next(it)
except StopIteration as e:
raise MyException("Empty Generator") from e
yield from it

decorator version :

from functools import wraps
def check_if_empty_first(gen):
@wraps(gen)
def inner(*args, **kwargs
it = gen(*args, **kwargs)
try:
yield next(it)
except StopIteration as e:
raise MyException("Empty Generator") from e
yield from it
return inner


Related Topics



Leave a reply



Submit