Decorators in Python allow you to dynamically inject new functionality into a function without altering its internal logic. This powerful metaprogramming technique promotes clean, modular code by letting you reuse standard utilities like logging, authentication, and caching across multiple functions.

Basic decorator structure:

def my_decorator(func): def wrapper(): print("Something is happening BEFORE the function is called.") func() # Call the original function  print("Something is happening AFTER the function is called.") return wrapper @my_decorator def say_hello(): print("Hello!") say_hello() 

Output:

Something is happening BEFORE the function is called. Hello! Something is happening AFTER the function is called. 
  • If the function you are decorating accepts arguments, your inner wrapper function must be built to accept and pass them through using *args and **kwargs. This ensures your decorator remains flexible and can work on any function.
def log_arguments(func): def wrapper(*args, **kwargs): print(Log: Function '{func.__name__}' was called with inputs: {args} {kwargs}") return func(*args, **kwargs) # Capture and return the original function's result return wrapper @log_arguments def add_numbers(a, b): return a + b result = add_numbers(5, 10) 

Real world application of Decorators

def pandas_decorator(fx): def mainfunc(*args): response = fx(*args) # Do some pandas specific processing here  response.to_parquet("Ch-3_Polymorphism&Decorators\\temp.parquet") return response return mainfunc @pandas_decorator def csv_to_parquet(file_path:str): import pandas as pd df = pd.read_csv(file_path) return df response = csv_to_parquet("Ch-3_Polymorphism&Decorators\\orders.csv") print(response.head()) 
  • This code demonstrates how Python decorators can be used to add extra functionality to an existing function without changing its original logic. The pandas_decorator(fx) function takes another function as an argument, while its inner function, mainfunc(*args), accepts the arguments passed to the original function and executes it using fx(*args). The returned result is stored in response, which is a Pandas DataFrame. The decorator then uses response.to_parquet() to save the DataFrame as a Parquet file named temp.parquet before returning the DataFrame. The @pandas_decorator syntax applies this decorator to the csv_to_parquet() function, which uses pd.read_csv(file_path) to load data from orders.csv. Finally, response = csv_to_parquet(...) executes the decorated function, reads the CSV file, saves the data as Parquet, and returns the DataFrame, while print(response.head()) displays its first five rows. In summary, the decorator automates saving the DataFrame to Parquet while allowing the original function to continue returning the data for further analysis.