0% found this document useful (0 votes)
51 views3 pages

Python Iterators, Generators & Decorators

This cheat sheet provides an overview of Python iterators, generators, and decorators, including their definitions, use cases, and examples. It highlights best practices for implementing these concepts and includes ready-to-use code snippets for infinite generators and retry decorators. The document serves as a quick reference for enhancing Python programming skills in these areas.

Uploaded by

arijitrajiv
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)
51 views3 pages

Python Iterators, Generators & Decorators

This cheat sheet provides an overview of Python iterators, generators, and decorators, including their definitions, use cases, and examples. It highlights best practices for implementing these concepts and includes ready-to-use code snippets for infinite generators and retry decorators. The document serves as a quick reference for enhancing Python programming skills in these areas.

Uploaded by

arijitrajiv
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

Python Iterators, Generators & Decorators - Expert Cheat Sheet

Complete Expert Cheat Sheet

ITERATORS
-------------
Definition:
An object with __iter__() and __next__() methods.

Use Case:
- Custom data structures
- Controlled iteration
- Wrapping complex data sources

Example:
class CountUpTo:
def __init__(self, max):
[Link] = max
[Link] = 1
def __iter__(self):
return self
def __next__(self):
if [Link] > [Link]:
raise StopIteration
val = [Link]
[Link] += 1
return val

for i in CountUpTo(3): print(i)

Best Practices:
- Raise StopIteration cleanly
- Use iter(), zip(), enumerate(), reversed()

GENERATORS
---------------
Definition:
A function that yields values instead of returning them.

Use Case:
- Stream processing
- Lazy evaluation
- Pipelines

Example:
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1

for num in count_up_to(3): print(num)


Python Iterators, Generators & Decorators - Expert Cheat Sheet

Generator Expression:
squares = (x * x for x in range(5))

Best Practices:
- Use yield for memory-efficiency
- Combine with next(), for loop
- Clear documentation

DECORATORS
---------------
Definition:
Functions that modify/enhance other functions.

Use Case:
- Logging
- Authentication
- Input validation
- Caching, Timing

Basic Example:
def decorator(func):
def wrapper(*args, **kwargs):
print("Before")
result = func(*args, **kwargs)
print("After")
return result
return wrapper

@decorator
def greet():
print("Hello")

With Arguments:
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator

@repeat(3)
def say_hi(): print("Hi")

With [Link] (Best Practice):


from functools import wraps
def log_call(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
Python Iterators, Generators & Decorators - Expert Cheat Sheet
return func(*args, **kwargs)
return wrapper

Industrial Use Cases


--------------------------
- Read files line-by-line (Generator)
- Paginated API fetch (Iterator)
- Function logging (Decorator)
- Retry logic (Decorator)
- Data pipelines (Generator)

Ready-to-Use Code
--------------------------
Infinite Generator:
def infinite_counter(start=0):
while True:
yield start
start += 1

Retry Decorator:
def retry(n):
def decorator(func):
def wrapper(*args, **kwargs):
for attempt in range(n):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Attempt {attempt+1} failed: {e}")
raise Exception("All retries failed")
return wrapper
return decorator

Common questions

Powered by AI

Iterators in Python improve handling of custom data structures by allowing controlled iteration over complex datasets without exposing underlying details. This is achieved through the implementation of the __iter__() and __next__() methods in classes. Best practices for implementing iterators include raising StopIteration cleanly to signal the end of iteration and utilizing built-in iterator functions such as iter(), zip(), enumerate(), and reversed() for effective and concise control over iteration processes .

Python decorators can enhance logging functionality by wrapping functions to insert pre-defined logging behavior before and/or after the function execution. Using decorators, logging calls can be added without altering the core function logic. A decorator like log_call utilizes the functools.wraps utility to maintain the original function's metadata while printing log messages each time the function is invoked. This allows developers to uniformly apply logging across multiple functions in an application, improving maintainability and reducing repetitive code .

The 'yield' statement in Python generators allows a function to produce a series of values over time, rather than computing them all and sending them back at once. This lazy evaluation approach means the generator only calculates new values as needed, which conserves memory as it does not require storing the entire dataset in memory at once. This makes generators particularly useful for stream processing and data pipeline tasks, enabling operations on potentially infinite data streams without overwhelming system resources .

The infinite generator in Python can be implemented using a while loop combined with 'yield' to produce an endless sequence of values. An example is an infinite counter that starts from a given number and yields successive integers indefinitely. This is particularly useful in real-time processing tasks or simulations where a continuous input stream is required without predefined limits, such as generating unique IDs, streamlining data for real-time analytics, or marching through data at runtime without constraints .

Iterators in Python require implementing both __iter__() and __next__() methods, which can add to implementation complexity, especially for stateful iteration. They are useful for custom data structures and allow precise control over the iteration process. In contrast, generators are functions that use 'yield' to produce iterables and typically involve simpler code for equivalent tasks since state management between yields is handled automatically. Generators excel in stream processing and scenarios involving lazy evaluation, providing a more straightforward syntax for producing and consuming data on-the-fly .

Iterators and generators support lazy evaluation by computing values on-demand rather than pre-computing and storing them, which saves memory and processing power. In Python, iterators provide controlled access to elements, advancing only when required, while generators use 'yield' to produce successive items only when needed. Typical use cases include processing large datasets or streams where storing all data at once would be impractical, such as reading files line-by-line, handling paginated API responses, or performing complex calculations on-the-fly in data pipelines .

It is recommended to use functools.wraps when defining decorators to maintain the original function's metadata such as its name, docstring, and module. Without functools.wraps, decorators could obscure the metadata of the decorated function, leading to issues with introspection methods and debugging. By preserving this metadata, wraps ensure that the decorator is transparent in terms of function attributes and can help maintain code readability and usability, especially in documentation and logging contexts .

Decorators with arguments provide added flexibility by allowing customization of the decorator’s behavior. This means that decorators can be parameterized to, for example, repeat a function call multiple times, set retry limits, or conditionally apply functionality based on the provided argument. An example where this is beneficial is in retry logic: a retry(n) decorator can automatically reattempt a function call a specified number of times if it fails, which is useful in network programming where transient issues may cause temporary failures .

Function caching via decorators enhances performance by storing the results of expensive function calls and returning the cached result when the same inputs occur again. This reduces the need to rerun identical computations, saving time and computational resources. Applicable scenarios include recursive functions with overlapping subproblems, like Fibonacci calculations, or functions making repeated network or database requests, where the cost of recalculating or fetching data is high compared to accessing it from a cache .

Retry decorators are valuable in error-prone operations as they provide a mechanism to automatically re-attempt function executions upon failure, thus improving robustness and reliability. They operate by wrapping the target function and executing it within a loop that iterates a specified number of times. If an exception occurs, the decorator catches it, logs the attempt, and retries up to the given limit. This pattern is especially useful in operations prone to transient errors, like network requests, where retrying can increase the probability of success without manual intervention .

You might also like