Python Empty Generator Function

2019-03-09 05:35发布

问题:

In python, one can easily define an iterator function, by putting the yield keyword in the function's body, such as:

def gen():
    for i in range(100):
        yield i

How can I define a generator function that yields no value (generates 0 values), the following code doesn't work, since python cannot know that it is supposed to be an generator and not a normal function:

def empty():
    pass

I could do something like

def empty():
    if False:
        yield None

But that would be very ugly. Is there any nice way to realize an empty iterator function?

回答1:

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. 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.



回答2:

iter(())

You don't require a generator. C'mon guys!



回答3:

Python 3.3 (because I'm on a yield from kick, and because @senderle stole my first thought):

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

But I have to admit, I'm having a hard time coming up with a use case for this for which iter([]) or (x)range(0) wouldn't work equally well.



回答4:

Another option is:

(_ for _ in ())


回答5:

I prefer the following:

def foo():
  raise StopIteration()
  yield

The "yield" turns it into a generator while Exception means None isn't included in the result (purely empty result).



回答6:

Must it be a generator function? If not, how about

def f():
    return iter([])


回答7:

The "standard" way to make an empty iterator appears to be iter([]). I suggested to make [] the default argument to iter(); this was rejected with good arguments, see http://bugs.python.org/issue25215 - Jurjen



回答8:

generator = (item for item in [])


回答9:

For those of you that actually need a function and actually need a generator

empty = lambda: (_ for _ in ())