Generator functions allow for yielding which is an important skill to have when you are becoming advanced with python. It basically allows creating lists but not caring about the whole list, only about the next number. This allows for time savings in many situations.

Anyhow, these are just my own practice notes on the yield call. I don’t have any clever explanations for it. Besides that when the code executes yield, it leaves the function back to where it was called (in the example its the list function), then when it goes back in (in the example: the list function will attempt to go thru the entirety of the function being yielded), it remembers at which line it was and continues (it keeps on looping). For your own exercise, you can add print() methods to see how it proceeds.

``````# GENERATOR RANGE EXAMPLE WITH list()

def r1(low,high):
x = low
while x <= high:
yield x
x += 1

def r2(low,high):
x = low - 1
while x < high:
x += 1
yield x

print(f"{list(r1(10,20))=}")
print(f"{list(r2(10,20))=}")

# BOTH RETURN:
# list(r1(10,20))=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
# list(r2(10,20))=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]``````

Also, the list() method is not the best way to show yields, as its simply prints everything the generator is trying to express. This could have been done with none yielding code that appends a new number to a list and returns a list.

Instead, we can really show the power of these generator functions using the next() method.

``````# GENERATOR EXAMPLE WITH next()

new_range = r1(6,8)
print(f"{new_range=}")           # always prints address of the generator object
print(f"{next(new_range)=}")     # gives first number 6
print(f"{next(new_range)=}")     # gives next number 7
print(f"{next(new_range)=}")     # gives next number 8
print(f"{next(new_range)=}")     # gives next number 8 - ERROR

# OUTPUT

# new_range=<generator object r1 at 0x0000020A6D4B5EB0>
# next(new_range)=6
# next(new_range)=7
# next(new_range)=8
#   File "D:\Dropbox\src\fb-centaur\yield_practice_created_range.py", line 23, in <module>
#     print(f"{next(new_range)=}")   # gives next number 8 - ERROR
# StopIteration``````

Note that we define the new_range variable which becomes a generator object. Then if we were to call list() on this object we would get [6,7,8] – not shown here as similar concept is shown in the first code output. However, if we call next(new_range), we get the next value of this generator – the key concept is that if it hasn’t been called yet it will provide the first value of the generator. So the first next() will output the first number, which is 6. Then 7. Then 8. If we call it again, it will error as it’s the end of the generator object.

Benefits: Imagine the range was r1(1,100000000). Then imagine we needed the 1000th item. We could generate the list() of it and then get the 1000th item, but that will take a while to generate the list. So instead we can just yield thru until the 1000th item without caring about the rest. Speed ðŸ™‚

Thats all folks.