codingstuff.io
ExploreTutorialsProblemsCS Subjects
Get Started
ExploreTutorialsProblemsCS Subjects
Get Started
codingstuff.io

Master the art of building software through interactive tutorials, real-world problems, and guided projects.

Pune, Maharashtra, India

codingstuffmail@gmail.com

Product

  • Explore
  • Tutorials
  • Problems
  • CS Subjects

Company

  • About
  • Contact
  • Privacy Policy
  • Terms & Conditions
  • Sitemap

© 2026 codingstuff.io. All rights reserved.

Built with ❤️ for developers everywhere

/
/
All Tutorials
🐍

Python Programming

38 / 68 topics
36List Comprehensions37Python Iterators38Python Generators39Python Decorators40Python Modules, Packages & PIP41Python Main Function (__name__ == '__main__')42Python Dates and Time43Python Regular Expressions44Python JSON
Tutorials/Python Programming/Python Generators
🐍Python Programming

Python Generators

Updated 2026-05-15
30 min read

Python Generators

Generators are a powerful feature in Python that allow you to create iterators in a more convenient way. They provide lazy evaluation, which means they generate items on-the-fly as you iterate over them, rather than storing the entire sequence in memory. This makes generators highly memory efficient, especially when dealing with large datasets.

In this tutorial, we'll dive into generator functions using the yield keyword, explore generator expressions, and learn about advanced methods like send() and throw(). By the end of this guide, you'll be able to write efficient and flexible code using Python generators.

Introduction

Generators are a type of iterable that can be used in loops. Unlike lists or tuples, which store all their elements in memory at once, generators generate each element on-the-fly as you iterate over them. This lazy evaluation makes generators ideal for working with large datasets or streams of data without consuming excessive memory.

Generator Functions with yield

A generator function is defined like a regular function but uses the yield keyword instead of return. When a generator function is called, it returns a generator object that can be iterated over. Each time you iterate over the generator, the function runs until it hits a yield statement, which returns the yielded value and pauses the function's execution.

Example 1: Basic Generator Function

Let's start with a simple example of a generator function that generates numbers from 0 to 4:

basic_generator.py
1def count_up_to(max):
2 count = 0
3 while count < max:
4 yield count
5 count += 1
6
7# Using the generator
8for number in count_up_to(5):
9 print(number)
Output
0
1
2
3
4

In this example, the count_up_to function is a generator that yields numbers from 0 to max - 1. Each time you iterate over the generator using a loop, the function resumes execution at the point where it left off and continues until it hits another yield.

Example 2: Using next() to Manually Iterate

You can also manually control the iteration process by calling the next() function on the generator object:

manual_iteration.py
1def count_up_to(max):
2 count = 0
3 while count < max:
4 yield count
5 count += 1
6
7# Creating a generator object
8gen = count_up_to(5)
9
10print(next(gen)) # Output: 0
11print(next(gen)) # Output: 1
12print(next(gen)) # Output: 2
Output
0
1
2

Using next() allows you to control the iteration process more precisely. When there are no more items to yield, calling next() will raise a StopIteration exception.

Lazy Evaluation and Memory Efficiency

One of the key advantages of generators is their lazy evaluation. They only generate items as needed, which makes them highly memory efficient. This is particularly useful when working with large datasets or infinite sequences.

Example 3: Generating an Infinite Sequence

Let's create a generator that generates an infinite sequence of Fibonacci numbers:

infinite_fibonacci.py
1def fibonacci():
2 a, b = 0, 1
3 while True:
4 yield a
5 a, b = b, a + b
6
7# Using the generator to get the first 10 Fibonacci numbers
8fib_gen = fibonacci()
9for _ in range(10):
10 print(next(fib_gen))
Output
0
1
1
2
3
5
8
13
21
34

In this example, the fibonacci generator generates Fibonacci numbers indefinitely. The sequence is only generated as needed, so you can control how many numbers to generate without consuming excessive memory.

Generator Expressions

Generator expressions are a concise way to create generators using a syntax similar to list comprehensions. They use parentheses instead of square brackets and automatically yield items one at a time.

Example 4: Basic Generator Expression

Here's an example of a generator expression that generates the squares of numbers from 0 to 9:

basic_generator_expression.py
1squares = (x * x for x in range(10))
2
3# Using the generator expression
4for square in squares:
5 print(square)
Output
0
1
4
9
16
25
36
49
64
81

Generator expressions are memory efficient because they generate each item on-the-fly and don't store the entire sequence in memory.

Example 5: Using sum() with a Generator Expression

You can also use generator expressions with built-in functions like sum() to perform operations on generated items:

sum_with_generator_expression.py
1total = sum(x * x for x in range(10))
2print(total)
Output
285

In this example, the generator expression generates the squares of numbers from 0 to 9, and sum() adds them up. The entire sequence is never stored in memory, making this approach efficient.

Advanced Methods: send() and throw()

Generators have two advanced methods that allow you to interact with the generator during its execution: send() and throw().

send(value)

The send() method allows you to send a value back into the generator, which becomes the result of the yield expression. This can be used to control the flow of the generator from outside.

Example 6: Using send()

Here's an example that demonstrates how to use send():

using_send.py
1def coroutine():
2 while True:
3 received = yield
4 print(f"Received: {received}")
5
6# Creating a generator object and starting the coroutine
7coro = coroutine()
8next(coro) # Prime the coroutine
9
10# Sending values to the coroutine
11coro.send("Hello")
12coro.send(123)
Output
Received: Hello
Received: 123

In this example, the coroutine generator is a simple echo that prints received values. The send() method sends values into the generator, which are then printed.

throw(exception)

The throw() method allows you to raise an exception inside the generator. This can be used to handle errors or control the flow of the generator from outside.

Example 7: Using throw()

Here's an example that demonstrates how to use throw():

using_throw.py
1def error_handler():
2 try:
3 while True:
4 yield
5 except ValueError as e:
6 print(f"Caught an exception: {e}")
7
8# Creating a generator object and starting the coroutine
9err_gen = error_handler()
10next(err_gen) # Prime the coroutine
11
12# Raising an exception inside the generator
13err_gen.throw(ValueError("Something went wrong"))
Output
Caught an exception: Something went wrong

In this example, the error_handler generator catches a ValueError raised by throw() and prints a message.

Practical Example

Let's put everything together in a practical example. We'll create a generator that reads lines from a file lazily, allowing us to process large files without loading them entirely into memory.

Example 8: Lazy File Reader Generator

Here's the code for the lazy file reader generator:

lazy_file_reader.py
1def read_large_file(file_path):
2 with open(file_path, 'r') as file:
3 for line in file:
4 yield line.strip()
5
6# Using the generator to read a large file
7file_path = "large_file.txt"
8for line in read_large_file(file_path):
9 print(line)

In this example, the read_large_file generator reads lines from a specified file one at a time. This approach is memory efficient because it doesn't load the entire file into memory.

Summary

  • Generator functions use the yield keyword to generate items on-the-fly.
  • Lazy evaluation makes generators highly memory efficient, especially for large datasets.
  • Generator expressions provide a concise way to create generators using syntax similar to list comprehensions.
  • The send() method allows you to send values back into the generator.
  • The throw() method allows you to raise exceptions inside the generator.

What's Next?

In the next tutorial, we'll explore Python decorators. Decorators are a powerful feature that allow you to modify or enhance the behavior of functions and methods without changing their code. They provide a flexible way to add functionality like logging, access control, or memoization. Stay tuned for more advanced Python topics!


PreviousPython IteratorsNext Python Decorators

Recommended Gear

Python IteratorsPython Decorators