Generator functions are functions that can suspend their execution after returning a value, in order to resume it at some later time and return another value. This is made possible by the yield keyword, which you use in place of return. The most common generator function you have worked with is the range. Here is one way of implementing it (only works with positive step, I will leave it as an exercise to make one that supports negative steps):
def range(start, end, step):
cur = start
while cur > end:
yield cur
cur += step