1

A generator function:

def getPositiveCount():
    n = 0
    while n<100:
        yield n
        n+=1

The generator function above does not do the expected when called in a next(..) method. Why does the next call return 0 all the times?

print(next(getPositiveCount())) # prints 0
print(next(getPositiveCount())) # prints 0; should've printed 1
print(next(getPositiveCount())) # prints 0; should've printed 2

However, it works just fine if I set it up in a loop:

for i in getPositiveCount():
    print(i)

# prints 0,1,2 ... 99
    

This as well, again, makes me ask, are generator functions the same as generator expressions (that normally uses a range, xrange or similar kind of builtins)? e.g. the next call in a generator expression works perfectly:

squares = (n** 2 for n in range(5))
print(next(squares)) # 0
print(next(squares)) # 1
print(next(squares)) # 4
martineau
  • 119,623
  • 25
  • 170
  • 301
edam
  • 910
  • 10
  • 29
  • Sidenote: it's simpler to do `for n in range(100): yield n` – wjandrea Dec 29 '21 at 21:43
  • `range(100)` doesn't occupy memory? @wjandrea – edam Dec 29 '21 at 21:50
  • It does not. You might be thinking [of Python 2](/a/94962/4518341). – wjandrea Dec 29 '21 at 21:52
  • @wjandrea is there any way to see that it does not occupy memory in python3, for example using reflection? – edam Dec 29 '21 at 21:54
  • 1
    You can just read the docs: [Ranges](https://docs.python.org/3/library/stdtypes.html#ranges) *"The advantage of the range type over a regular list or tuple is that a range object will always take the same (small) amount of memory, no matter the size of the range it represents (as it only stores the `start`, `stop` and `step` values, calculating individual items and subranges as needed)."* – wjandrea Dec 29 '21 at 21:59
  • Are you similarly surprised that `print([1,2,3].pop())` gives you the same result every time, even though `.pop` changes the list? Same problem here. – Karl Knechtel Dec 29 '21 at 22:01
  • @KarlKnechtel to me it's obvious why it should return 3 every time, but not in the generator function's case, cause I could actually use it like an iterator in a for loop but not as in with the next call individually, that naturally confuses. – edam Dec 29 '21 at 22:04
  • That's because the `for` loop doesn't call `getPositiveCount` every time through the loop, just like it wouldn't for an ordinary function. A generator function is not "function syntax that defines a generator object"; it *returns* a generator object ("generator iterator", as called in the [official documentation](https://docs.python.org/3/glossary.html#term-generator), which is where you [should](https://meta.stackoverflow.com/questions/261592) start looking in general). – Karl Knechtel Dec 29 '21 at 22:12
  • ([This](https://docs.python.org/3/reference/expressions.html#yield-expressions) might be a better documentation link, depending on your exact mental model.) – Karl Knechtel Dec 29 '21 at 22:13

1 Answers1

8

Each call to getPositiveCount() creates a brand new generator. You are thinking of this:

gen = getPositiveCount()
print(next(gen))
print(next(gen))
print(next(gen))
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Tim Roberts
  • 48,973
  • 4
  • 21
  • 30