0% found this document useful (0 votes)
8 views

Decorators and Generators in Python

The document provides a detailed exploration of decorators and generators in Python, highlighting their mechanics, applications, and significance. Decorators enhance function behavior without altering code, promoting clean and reusable programming, while generators allow for memory-efficient, lazy value production. Mastery of these tools is essential for writing efficient and modular Python code in various domains.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

Decorators and Generators in Python

The document provides a detailed exploration of decorators and generators in Python, highlighting their mechanics, applications, and significance. Decorators enhance function behavior without altering code, promoting clean and reusable programming, while generators allow for memory-efficient, lazy value production. Mastery of these tools is essential for writing efficient and modular Python code in various domains.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

Detailed Report on Decorators and Generators in Python

Python, known for its simplicity and elegance, provides robust tools for advanced functionality.
Among these, decorators and generators stand out for their ability to enhance modularity and
efficiency in programming. This detailed exploration delves deeper into their mechanics, applications,
and significance.

Decorators in Python
1. What Are Decorators?
Decorators are a design pattern in Python used to modify or extend the behaviour of functions or
methods without altering their code. They wrap another function, executing additional logic before
and/or after the wrapped function executes.

This makes decorators a powerful tool for maintaining clean and reusable code by separating concerns
like logging, authentication, or caching from core logic.

2. Anatomy of a Decorator
A decorator is essentially a function that:
1. Accepts another function (or method) as its argument.

2. Defines an inner function (often called wrapper) that performs additional operations.

3. Calls the original function within the wrapper.

4. Returns the wrapper function.


Example
python
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
print(f"Wrapper executed before {original_function.__name__}.")
result = original_function(*args, **kwargs)
print(f"Wrapper executed after {original_function.__name__}.")
return result
return wrapper_function

@decorator_function
def display_message(message):
print(message)
display_message("Hello, Python!")
Output:
Wrapper executed before display_message.
Hello, Python!
Wrapper executed after display_message.

3. Benefits of Decorators
1. Code Reusability: Reuse the same decorator across multiple functions.
2. Separation of Concerns: Keep the core logic independent of auxiliary tasks like logging
or validation.
3. Dynamic Behavior: Modify the behavior of functions without altering their definitions.

4. Common Use Cases


a. Logging
Track execution details like input parameters and runtime performance.
python

def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"Function `{func.__name__}` called with arguments: {args},
{kwargs}")
return func(*args, **kwargs)
return wrapper

@log_decorator
def add(x, y):
return x + y

print(add(3, 5))

b. Access Control
Enforce user permissions or role-based access.
python

def role_required(role):
def decorator(func):
def wrapper(user_role, *args, **kwargs):
if user_role != role:
print(f"Access denied. `{role}` role required.")
return
return func(*args, **kwargs)
return wrapper
return decorator
@role_required('admin')
def sensitive_task(*args):
print("Sensitive task executed.")

sensitive_task('user')
sensitive_task('admin')

c. Memoization
Cache results of expensive computations to improve performance.
python
Copy code
def memoize(func):
cache = {}
def wrapper(n):
if n not in cache:
cache[n] = func(n)
return cache[n]
return wrapper

@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))

5. Built-in Decorators
1. @staticmethod: Defines a method that doesn't depend on instance attributes.

2. @classmethod: Defines a method bound to the class rather than the instance.

3. @property: Converts a method into a read-only attribute.

6. Advanced Use: Class Decorators


Decorators can also be implemented as classes, allowing them to maintain state.
python
Copy code
class Counter:
def __init__(self, func):
self.func = func
self.count = 0

def __call__(self, *args, **kwargs):


self.count += 1
print(f"{self.func.__name__} called {self.count} times")
return self.func(*args, **kwargs)
@Counter
def greet(name):
print(f"Hello, {name}!")

greet("Alice")
greet("Bob")

Generators in Python
1. What Are Generators?
Generators are a special type of iterable that lazily produce values one at a time. Unlike functions
that return values using return, generators use the yield keyword to produce values and
suspend their execution state.

2. How Generators Work


A generator function returns a generator object. Each call to its next() method executes the
function until the next yield, returning the value yielded. Execution resumes from that point on
subsequent calls.
Example
python
Copy code
def count_up_to(max_value):
count = 1
while count <= max_value:
yield count
count += 1

gen = count_up_to(3)
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3

3. Advantages of Generators
1. Memory Efficiency: Only compute values as needed, reducing memory overhead.
2. Simpler Code: Eliminate the need for managing complex states manually.
3. Infinite Sequences: Ideal for producing sequences of indeterminate size.

4. Applications
a. Reading Large Files
Efficiently read large files line by line without loading the entire file into memory.
python
Copy code
def read_large_file(file_path):
with open(file_path) as file:
for line in file:
yield line.strip()

for line in read_large_file("large_text_file.txt"):


print(line)

b. Infinite Sequences
Generate an infinite stream of Fibonacci numbers.
python
Copy code
def infinite_fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b

fib = infinite_fibonacci()
for _ in range(10):
print(next(fib))

c. Data Pipelines
Process data in real-time using generators.
python
Copy code
def data_source():
for i in range(1, 6):
yield i

def data_processor(data):
for item in data:
yield item ** 2

pipeline = data_processor(data_source())
for result in pipeline:
print(result)

5. Generator Expressions
Generator expressions provide a concise way to create generators, similar to list comprehensions.
Example
python
Copy code
squares = (x**2 for x in range(5))
for square in squares:
print(square)
Conclusion
Decorators and generators exemplify Python's philosophy of "simple yet
powerful." Decorators provide a clean way to augment function behaviour, while
generators offer an efficient mechanism for producing and processing data lazily.
Mastery of these constructs enables developers to write cleaner, more efficient, and
more modular Python code. These tools are indispensable in modern Python
programming, especially in areas like web development, data science, and
real-time systems.

You might also like