You’re the Expert!

pynfinity

Build mountain with each pebble 🧗

Topics | Stepping Stones

📚 Guides

🌍 Pebbles & Contributions

✍️ Write a Post
🐼 Pandas DataFrames
🐼 Pandas DataFrames
🔢 NumPy Arrays
🔢 NumPy Arrays
📉 Matplotlib Plotting
📉 Matplotlib Plotting
🔥 Seaborn Heatmaps
🔥 Seaborn Heatmaps
🤖 Scikit-Learn Linear Regression
🤖 Scikit-Learn Linear Regression
🧹 Data Cleaning with Dropna
🧹 Data Cleaning with Dropna
🔍 Exploratory Data Analysis (EDA)
🔍 Exploratory Data Analysis (EDA)
⏳ Time Series Resampling
⏳ Time Series Resampling
🕸️ Web Scraping with BeautifulSoup
🕸️ Web Scraping with BeautifulSoup
🗄️ SQLAlchemy Basics
🗄️ SQLAlchemy Basics
📊 Interactive Plots with Plotly
📊 Interactive Plots with Plotly
📝 NLTK Tokenization
📝 NLTK Tokenization
🧠 TensorFlow Basics
🧠 TensorFlow Basics
🔥 PyTorch Tensors
🔥 PyTorch Tensors
📉 Statsmodels OLS
📉 Statsmodels OLS
📸 OpenCV Image Reading
📸 OpenCV Image Reading
🕸️ NetworkX Graphs
🕸️ NetworkX Graphs
🗺️ Folium Maps
🗺️ Folium Maps
🚀 Streamlit Apps
🚀 Streamlit Apps
⚡ FastAPI Endpoints
⚡ FastAPI Endpoints
✨ Jupyter Magic Commands
✨ Jupyter Magic Commands
📦 Virtual Environments
📦 Virtual Environments
🌲 Git Basics
🌲 Git Basics
🐳 Dockerfiles
🐳 Dockerfiles
☁️ AWS S3 with Boto3
☁️ AWS S3 with Boto3
🧩 Regular Expressions
🧩 Regular Expressions
λ Lambda Functions
λ Lambda Functions
📜 List Comprehensions
📜 List Comprehensions
⚡ Generators
⚡ Generators
🎀 Decorators
🎀 Decorators
🚪 Context Managers
🚪 Context Managers
🧵 Multithreading
🧵 Multithreading
🎛️ Multiprocessing
🎛️ Multiprocessing
⏳ AsyncIO
⏳ AsyncIO
🏷️ Type Hinting
🏷️ Type Hinting
📦 Dataclasses
📦 Dataclasses
🛡️ Pydantic Models
🛡️ Pydantic Models
🧪 Pytest Testing
🧪 Pytest Testing
🪵 Logging
🪵 Logging
💻 Argparse CLI
💻 Argparse CLI
📄 JSON Handling
📄 JSON Handling
📊 CSV Processing
📊 CSV Processing
🥒 Pickle Serialization
🥒 Pickle Serialization
🖥️ OS Module
🖥️ OS Module
⚙️ Sys Module
⚙️ Sys Module
📚 Collections Module
📚 Collections Module
🔁 Itertools
🔁 Itertools
🛠️ Functools
🛠️ Functools
➗ Math Module
➗ Math Module
+ New Post

🎨 Advanced Decorators in Python

Decorators are a powerful and expressive feature in Python that allows you to modify the behavior of functions or classes. While basic decorators are common, advanced techniques unlock even more potential.


🧠 What are Decorators?

At their core, decorators are functions that take another function and extend its behavior without explicitly modifying it.

@my_decorator
def my_function():
    pass

Is syntactic sugar for:

def my_function():
    pass
my_function = my_decorator(my_function)

🧩 Decorators with Arguments

To pass arguments to your decorator, you need to add another layer of wrapping.

import functools

def repeat(num_times):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"Hello {name}")

greet("World")
# Output:
# Hello World
# Hello World
# Hello World

🏗 Class Decorators

Decorators can also be applied to classes. This is useful for adding functionality to every method or modifying class attributes.

def singleton(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class DatabaseConnection:
    def __init__(self):
        print("Loading database...")

db1 = DatabaseConnection()
db2 = DatabaseConnection()
# "Loading database..." prints only once
print(db1 is db2)  # True

💾 Stateful Decorators

You can use a class as a decorator to maintain state.

class CountCalls:
    def __init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__!r}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")

say_hello()
say_hello()
# Output:
# Call 1 of 'say_hello'
# Hello!
# Call 2 of 'say_hello'
# Hello!

🔄 Nested Decorators

You can stack multiple decorators on a single function. They are applied from bottom to top.

@debug
@do_twice
def greet(name):
    print(f"Hello {name}")

Equivalent to: greet = debug(do_twice(greet))


📝 Summary

  • Decorators with Arguments: Need three levels of nested functions.
  • Class Decorators: Can modify entire classes or manage instances (Singleton).
  • Stateful Decorators: Use classes with __call__ to keep state.
  • functools.wraps: Always use this to preserve metadata (name, docstring) of the original function.

Created with ❤️ by Pynfinity