Skip to content

7.2 Decorators and Generators – Understanding Decorators, Working with Generators

Welcome to an exciting lesson where we’ll explore two powerful features in Python: Decorators and Generators. Decorators provide a clean and efficient way to modify or extend the behavior of functions, while Generators offer a memory-efficient mechanism for creating iterators. In this lesson, we’ll dive deep into the concepts of decorators, understand their syntax and applications, and then move on to working with generators for efficient iteration.

Understanding Decorators:

1. Definition:

  • A decorator is a design pattern in Python that allows the modification or extension of the behavior of functions or methods without changing their actual code.

2. Syntax:

  • Decorators are created using the @decorator_name syntax above the function definition.
  • Example:
@my_decorator
def my_function():
    pass  # Placeholder for function implementation

3. Implementing Decorators:

  • Decorators are functions that take another function as an argument, perform some operation, and return a new function.
  • Example:
def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

Working with Generators:

4. Definition:

  • Generators are a special type of iterator in Python that allow the creation of iterators using a function with the yield keyword.

5. Generator Function:

  • A generator function is defined like a regular function but uses yield to produce a sequence of values.
  • Example:
def my_generator():
    yield 1
    yield 2
    yield 3

6. Using Generators:

  • Generators produce values on-the-fly, and their state is preserved between successive calls.
  • Example:
gen = my_generator()
print(next(gen))  # Outputs 1
print(next(gen))  # Outputs 2
print(next(gen))  # Outputs 3

Example:

Let’s create a practical example where we use a decorator to measure the execution time of a function and a generator to generate Fibonacci numbers.

# Example Code
import time

# Decorator for measuring execution time
def measure_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Execution time: {end_time - start_time} seconds")
        return result
    return wrapper

# Using the decorator
@measure_time
def slow_function():
    time.sleep(2)
    print("Function executed!")

# Generator for Fibonacci numbers
def fibonacci_generator():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# Using the generator
fibonacci_gen = fibonacci_generator()
print(next(fibonacci_gen))  # Outputs 0
print(next(fibonacci_gen))  # Outputs 1
print(next(fibonacci_gen))  # Outputs 1

Practice Exercise:

Create a Python script that uses a decorator to log the parameters and return value of a function and a generator to generate a sequence of even numbers.

# Example Practice Exercise
# Decorator for logging function parameters and return value
def log_function(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        print(f"Function {func.__name__} called with arguments {args} and keyword arguments {kwargs}.")
        print(f"Function {func.__name__} returned: {result}")
        return result
    return wrapper

# Using the decorator
@log_function
def add_numbers(a, b):
    return a + b

# Generator for even numbers
def even_numbers_generator():
    number = 0
    while True:
        yield number
        number += 2

# Using the generator
even_gen = even_numbers_generator()
print(next(even_gen))  # Outputs 0
print(next(even_gen))  # Outputs 2
print(next(even_gen))  # Outputs 4

Summary:

In this detailed lesson, we’ve explored the powerful features of decorators and generators in Python. Decorators provide a flexible way to modify or extend the behavior of functions, and generators offer memory-efficient iterators for on-the-fly value generation. Practice using decorators and generators to enhance your coding skills, and feel free to ask questions in the discussion forum. As we progress, we’ll continue to explore more advanced topics and techniques in Python!