Generators are a simple way of creating iterators. They are written using the yield keyword instead of return.
Here is an example of a generator that yields the first n squares:
def squares(n):
for i in range(n):
yield i * i
for square in squares(5):
print(square)
In this example, the squares function generates squares from 0 to n-1 using the yield keyword.
Write a generator that yields Fibonacci numbers up to a given limit.
Answers: Generators use the yield keyword and are more memory-efficient than iterators. They produce values one at a time and don't store them in memory.