Advancing Python Knowledge_1

API Rate Limiting

  • Rate limiting ensures you don’t overwhelm an API.
  • Use tools like time.sleep() to limit the frequency of API requests.

__enter__ and __exit__ for Context Management

  • Custom context managers can be created using __enter__ and __exit__.
  • Use with to ensure proper setup and teardown.

Timing Blocks for Performance Analysis

  • Use time or timeit to measure the performance of code blocks.

SQLAlchemy for Database Operations

  • SQLAlchemy provides an ORM for interacting with databases, and pyodbc is used for database connections.
  • Use create_engine to establish a database connection.
  • Connection Pooling: Helps manage connections efficiently.
  • Query Building: SQLAlchemy allows complex queries to be built using ORM or the SQL Expression Language.

Using pandas.read_sql() for SQL Queries

  • pandas.read_sql() allows you to run SQL queries directly and load the results into a DataFrame.

Dependency Injection: Avoid hardcoding dependencies by passing them into functions or classes.

class FileProcessor:
    def __init__(self, file_reader):
        self.file_reader = file_reader

class FileReader:
    def read(self):
        pass

Facade Pattern: Simplifies complex system interfaces by providing a higher-level interface.

class FileHandler:
    def process_file(self, filepath):
        # Complex operations
        pass

class FileFacade:
    def __init__(self):
        self.file_handler = FileHandler()
    
    def handle_file(self, filepath):
        self.file_handler.process_file(filepath)

The facade FileFacade is necessary because in this class, you can consolidate various way to process a file, making codes neat and dry.

decorator factory is a pattern where you create a function that returns a decorator. It’s useful when your decorator needs parameters — like a custom logger.

for example

# Function that we will decorate with our timer_decorator
@timer_decorator
def slow_function():
    print("Starting a slow function...")
    time.sleep(2)  # Simulate a slow operation (e.g., sleep for 2 seconds)
    print("Slow function finished.")

# Call the decorated function
slow_function()

This syntax is equivalent to slow_function = timer_decorator(slow_function). note the full function of decorator timer_decorator is

import time

# Decorator to measure the execution time of a function
def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()  # Record start time
        result = func(*args, **kwargs)  # Call the original function
        end_time = time.time()  # Record end time
        execution_time = end_time - start_time  # Calculate execution time
        print(f"Function {func.__name__} executed in {execution_time:.4f} seconds")
        return result  # Return the result of the original function
    return wrapper

Another great example involves three layers:

def log_execution_time(logger):
    def decorator(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            logger.info(f"{func.__name__} took {end_time - start_time:.3f} seconds to complete")
            return result
        return wrapper
    return decorator

All of them are useful! If I remove the outer layer, removing the outer layer limits flexibility and means the decorator cannot accept external parameters (like a logger).

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.