A Python generator is a “lazy Iterator” – a function that is a memory efficient way of providing an object to loop through
We can define a Python generator function just like a normal function. This article will show some simple examples to illustrate how easy they are to use and how effective they can be at improving the memory efficiency of your code.
We can use Yield to create a generator
We’ll also look at “generator comprehensions”
Why use a generator?
Generators are used in Python for several reasons, including:
- Efficient memory usage: Generators allow you to generate a sequence of values on-the-fly, rather than generating them all at once and storing them in memory. This is especially useful when dealing with large datasets, as it can save a significant amount of memory.
- Lazy evaluation: Generators are evaluated lazily, which means that the values are only generated when they are needed. This can improve performance and reduce unnecessary computations.
- Infinite sequences: Generators can be used to generate infinite sequences of values, which is not possible with regular lists or arrays.
- Simplify code: Generators can help simplify complex code by allowing you to express operations as a sequence of steps rather than nested loops.
- Improved readability: Generators can improve the readability of your code by making it more concise and easier to understand. They allow you to express the intent of your code more clearly, which can make it easier to maintain and modify in the future.
Overall, generators are a powerful and flexible feature of Python that can help you write more efficient, concise, and readable code.
You can read more at Python.org
Create a generator function
def my_generator(): yield "Line 1 from generator" yield "And line 2 from generator" obj = my_generator() print(type(obj))
The code shown above will return an object <class ‘generator’> as you can see below:
Iterate over the generator
We can iterate over the generator
for i in obj: print(i)
List Comprehension v Generator comprehension
The example below shows how much more memory efficient a python generator comprehension is compared to if we had returned a regular list.
import sys nums_squared_list_comprehension = [i ** 3 for i in range(100)] print(sys.getsizeof(nums_squared_list_comprehension)) nums_squared_generator_comprehension = (i ** 3 for i in range(100)) print(sys.getsizeof(nums_squared_generator_comprehension))
What is the difference?
We use “(” and “)” for the generator whereas the “[” and “]” we use for the list comprehension.
Use “next” with a Python generator to loop through one at a time
my_list = [1, 4, 5, 6, 7] def gen(some_list): for num in some_list: yield num genr = gen(my_list) print(type(genr)) print(next(genr)) # 0 print(next(genr)) # 1 print(next(genr)) # 5 print(next(genr)) # 6 print(next(genr)) # 7
We can see how generators save memory and the example above also demonstrates how we can use “Yield” in a function instead of “Return”. For example, if we had used return we would exit the function straight after the first value in some_list
In conclusion, using “Yield” allows us to keep adding the values to build the generator – a sequence of elements to iterate over – the “lazy iterator” rather than returning a large list or even one that is “too large”…
Thanks for reading, if you have any comments please feel free to do so!