Skip to content

Latest commit

 

History

History
249 lines (175 loc) · 5.95 KB

README.md

File metadata and controls

249 lines (175 loc) · 5.95 KB

Chapter 11: Iterators and Generators

Table of Contents

Introduction to Iterables and Iterators

In Python, an iterable is an object that can return an iterator. An iterator is an object that represents a stream of data. Iterators allow you to traverse through all the elements of a collection, one element at a time.

Example: Iterable

Lists, tuples, and strings are examples of iterables.

l = [1, 2, 3]

for i in l:
    print(i)

Output:

1
2
3

Explanation: The list l is an iterable, and we can use a for loop to iterate over its elements.

Creating Iterators with iter() and next()

You can create an iterator from an iterable using the iter() function. The next() function is used to get the next item from the iterator.

Example: Creating an Iterator

l = [1, 2, 3]
iter_l = iter(l)

print(next(iter_l))
print(next(iter_l))
print(next(iter_l))

Output:

1
2
3

Explanation: We create an iterator iter_l from the list l using the iter() function. We then use the next() function to get the next item from the iterator.

Handling StopIteration

When there are no more items to return, the next() function raises a StopIteration exception.

l = [1, 2, 3]
iter_l = iter(l)

while True:
    try:
        print(next(iter_l))
    except StopIteration:
        break

Output:

1
2
3

Explanation: We use a while loop and a try-except block to handle the StopIteration exception and stop the iteration.

Building Custom Iterators

You can create custom iterators by defining a class with __iter__ and __next__ methods.

Example: Custom Iterator

class CustomRange:
    def __init__(self, start, end, step=1):
        self.value = start
        self.end = end
        self.step = step

    def __iter__(self):
        return self

    def __next__(self):
        if self.value >= self.end:
            raise StopIteration

        current = self.value
        self.value += self.step
        return current

numbers = CustomRange(1, 5, 1)

for num in numbers:
    print(num)

Output:

1
2
3
4

Explanation: The CustomRange class defines a custom iterator that generates numbers from start to end with a given step. The __iter__ method returns the iterator object itself, and the __next__ method returns the next value in the sequence or raises a StopIteration exception when the end is reached.

Building Generators with yield

Generators are a simple way to create iterators. A generator is a function that uses the yield keyword to return values one at a time.

Example: Building a Generator

def custom_range(start, end, step=1):
    current = start
    while current < end:
        yield current
        current += step

numbers = custom_range(1, 5)

for num in numbers:
    print(num)

Output:

1
2
3
4

Explanation: The custom_range function is a generator that yields values from start to end with a given step. We use a for loop to iterate over the generated values.

Generator Expressions

Generator expressions provide a concise way to create generators. They are similar to list comprehensions but use parentheses instead of square brackets.

Example: Generator Expression

gen_exp = (x * x for x in range(5))

for num in gen_exp:
    print(num)

Output:

0
1
4
9
16

Explanation: The generator expression (x * x for x in range(5)) creates a generator that yields the squares of numbers from 0 to 4. We use a for loop to iterate over the generated values.

Use Cases for Generators

Generators are useful for working with large datasets, as they generate values on the fly and do not store them in memory. This makes them memory-efficient and suitable for tasks like reading large files or generating infinite sequences.

Example: Reading a Large File

def read_large_file(file_path):
    with open(file_path, 'r') as file:
        for line in file:
            yield line

for line in read_large_file('large_file.txt'):
    print(line.strip())

Explanation: The read_large_file function is a generator that reads a large file line by line. We use a for loop to iterate over the lines and print them.

Example: Generating an Infinite Sequence

def infinite_sequence():
    num = 0
    while True:
        yield num
        num += 1

gen = infinite_sequence()

for i in range(10):
    print(next(gen))

Output:

0
1
2
3
4
5
6
7
8
9

Explanation: The infinite_sequence function is a generator that yields an infinite sequence of numbers starting from 0. We use a for loop to get the first 10 numbers from the generator.

Summary

In this chapter, we covered iterables, iterators, and generators in Python. We learned how to create iterators using the iter() and next() functions, build custom iterators, build generators using the yield keyword, and use generator expressions for concise generator creation. We also explored use cases for generators, such as reading large files and generating infinite sequences.

Tasks

  1. Write a generator function that yields the first n Fibonacci numbers.
  2. Create a generator expression that generates the cubes of numbers from 1 to 10.
  3. Write a program that uses a generator to read a large file line by line and count the number of lines.
  4. Create a custom iterator class that iterates over a range of numbers with a given step.
  5. Write a generator function that generates an infinite sequence of even numbers.

Next Chapter: Conclusion and Further Learning