A generator function can be defined by putting the yield
keyword in the function’s body:
def gen(): for i in range(10): yield i
How to define an empty generator function?
The following code doesn’t work, since Python cannot know that it is supposed to be a generator function instead of a normal function:
def empty(): pass
I could do something like this:
def empty(): if False: yield
But that would be very ugly. Is there a nicer way?
Advertisement
Answer
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.