74

According to answer to this question, yield break in C# is equivalent to return in Python. In the normal case, return indeed stops a generator. But if your function does nothing but return, you will get a None not an empty iterator, which is returned by yield break in C#

def generate_nothing():
    return

for i in generate_nothing():
    print i

You will get a TypeError: 'NoneType' object is not iterable, but if I add and never run yield before return, this function returns what I expect.

def generate_nothing():
    if False: yield None
    return

It works, but seems weird. Do you have a better idea?

SuleymanSah
  • 17,153
  • 5
  • 33
  • 54
Sonic Lee
  • 1,411
  • 2
  • 14
  • 15
  • 10
    That's just how Python works. I don't think you really even need the final `return` in there. Python is not C#. don't expect them to work the same. – Chris Lutz Jun 18 '11 at 09:24
  • 4
    A function without `yield` is not a generator. Thus your first example just returns `None` and tries to iterate over that. – Jasmijn Jun 18 '11 at 09:36
  • @Jochen: that's not a generator. It's an iterable-producing object. Though as I said in phihag's answer, it's unlikely to matter. – Chris Morgan Jun 19 '11 at 11:56

4 Answers4

65
def generate_nothing():
    return
    yield
ninjagecko
  • 88,546
  • 24
  • 137
  • 145
51

A good way to handle this is raising StopIteration which is what is raised when your iterator has nothing left to yield and next() is called. This will also gracefully break out of a for loop with nothing inside the loop executed.

For example, given a tuple (0, 1, 2, 3) I want to get overlapping pairs ((0, 1), (1, 2), (2, 3)). I could do it like so:

def pairs(numbers):
    if len(numbers) < 2:
        raise StopIteration

    for i, number in enumerate(numbers[1:]):
        yield numbers[i], number

Now pairs safely handles lists with 1 number or less.

SHernandez
  • 1,060
  • 1
  • 14
  • 21
Sam Simmons
  • 1,074
  • 8
  • 12
  • 32
    note that from vs 3.6 and on this is a bad idea because with the implementation of [PEP 479](https://www.python.org/dev/peps/pep-0479/) the `StopIteration` will be converted into a `RuntimeError` to prevent problems when using sub generators. – Tadhg McDonald-Jensen May 18 '16 at 15:30
  • 28
    It's a bad idea in any version; just `return`. – Nick T Jun 14 '16 at 20:56
  • 15
    While this is functional (for now), `return` is better; the described functionality is even included in the [return documentation](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement). – MPlanchard Aug 31 '16 at 21:53
9
def generate_nothing():
    return iter([])
phihag
  • 278,196
  • 72
  • 453
  • 469
  • 3
    or `xrange(0)` (`range(0)` in 3.x). – Karl Knechtel Jun 18 '11 at 10:54
  • 2
    Both of these are about twice as fast as `return; yield`. But be aware that these will return objects of types `` and ``, both of which are direct subtypes of `object` - so they are not generators. Not that that's likely to matter. Merely of academic interest :-) – Chris Morgan Jun 18 '11 at 14:40
  • @Chris: in python3, I'm only clocking it about 20-30% faster, though qualitatively it seems slightly less of a strain of memory (not a rigorous test: `z=[list(f()) for _ in range(10**7)]`) – ninjagecko Jun 18 '11 at 21:53
4

The funny part is that both functions have the same bytecode. Probably there's a flag that sets to generator when bytecode compiler finds the yield keyword.

>>> def f():
...   return

>>> def g():
...   if False: yield 
#in Python2 you can use 0 instead of False to achieve the same result


>>> from dis import dis
>>> dis(f)
2           0 LOAD_CONST               0 (None) 
            3 RETURN_VALUE
>>> dis(g)
2           0 LOAD_CONST               0 (None) 
            3 RETURN_VALUE
JBernardo
  • 32,262
  • 10
  • 90
  • 115
  • Huh? They're far from the same. Check your results again. You made a mistake somewhere. – Chris Morgan Jun 18 '11 at 14:29
  • 1
    @Chris Yeah, Python2 don't optimize it because False isn't a keyword and might not be a false value. Python3 does. – JBernardo Jun 18 '11 at 14:53
  • Sweet. It doesn't even generates bytecode for yield statement –  Jun 18 '11 at 15:03
  • @JBernado: hmm, I see that that is possible in Python 3. I was thinking at first that that would be a glaring problem if False was assigned a different value. I'd forgotten that `True` and `False` are keywords in Python 3. And with the same bytecode, I presume the difference is in the fact that `f.__code__.co_flags == 67` while `g.__code__.co_flags == 99`. That suggests to be that the sixth bit flag marks it as a generator. Apologies for criticising you - but you really should mark it as Python 3 now for producing the same byte code, anyway. – Chris Morgan Jun 19 '11 at 11:54
  • @Chris I had already made a comment in my answer :) In Python 2 you can use 0 instead of False – JBernardo Jun 19 '11 at 17:11