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
Authenticate from Linux to Windows SQL Server with Pyodbc
Apt-Get Install for Different Python Versions
Why Is a List Comprehension So Much Faster Than Appending to a List
Show Default Value for Editing on Python Input Possible
How to Prevent Numbers Being Changed to Exponential Form in Python Matplotlib Figure
Computing Cross-Correlation Function
Loading .Rdata Files into Python
Socketserver.Threadingtcpserver - Cannot Bind to Address After Program Restart
Linux: Pipe into Python (Ncurses) Script, Stdin and Termios
Trying to Import a Module: Undefined Symbol: Pyunicodeucs4_Decodeutf8
Pil Installation Fails Missing:Stdarg.H
How to Set Explicitly the Terminal Size When Using Pexpect
Parsing Boolean Values with Argparse
How to Get Different Colored Lines for Different Plots in a Single Figure
Understanding Python Unicode and Linux Terminal