Python Decorators – Enhancing Functions with Powerful Wrappers

Decorators in Python allow us to modify the behavior of functions and classes without changing their actual code. They are a powerful tool for code reusability, logging, authentication, performance monitoring, and more.

In this post, we’ll cover:

What are decorators and how they work

Creating and using function decorators

Using built-in decorators like @staticmethod and @property

Real-world use cases for decorators

Let’s dive in!

1️⃣ Understanding Decorators in Python

What is a Decorator?

A decorator is a function that takes another function as input, enhances its behavior, and returns it.

Think of it like wrapping a function inside another function to modify its behavior dynamically.

Basic Example Without a Decorator

<span>def</span> <span>greet</span><span>():</span>
<span>return</span> <span>"</span><span>Hello, World!</span><span>"</span>
<span>print</span><span>(</span><span>greet</span><span>())</span> <span># Output: Hello, World! </span>
<span>def</span> <span>greet</span><span>():</span>
    <span>return</span> <span>"</span><span>Hello, World!</span><span>"</span>

<span>print</span><span>(</span><span>greet</span><span>())</span>  <span># Output: Hello, World! </span>
def greet(): return "Hello, World!" print(greet()) # Output: Hello, World!

Enter fullscreen mode Exit fullscreen mode

Now, let’s enhance this function by logging its execution.

Using a Simple Decorator

<span>def</span> <span>log_decorator</span><span>(</span><span>func</span><span>):</span>
<span>def</span> <span>wrapper</span><span>():</span>
<span>print</span><span>(</span><span>f</span><span>"</span><span>Calling function: </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span>"</span><span>)</span>
<span>result</span> <span>=</span> <span>func</span><span>()</span>
<span>print</span><span>(</span><span>f</span><span>"</span><span>Function </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span> executed successfully.</span><span>"</span><span>)</span>
<span>return</span> <span>result</span>
<span>return</span> <span>wrapper</span>
<span>@log_decorator</span> <span># Applying decorator </span><span>def</span> <span>greet</span><span>():</span>
<span>return</span> <span>"</span><span>Hello, World!</span><span>"</span>
<span>print</span><span>(</span><span>greet</span><span>())</span>
<span>def</span> <span>log_decorator</span><span>(</span><span>func</span><span>):</span>
    <span>def</span> <span>wrapper</span><span>():</span>
        <span>print</span><span>(</span><span>f</span><span>"</span><span>Calling function: </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span>"</span><span>)</span>
        <span>result</span> <span>=</span> <span>func</span><span>()</span>
        <span>print</span><span>(</span><span>f</span><span>"</span><span>Function </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span> executed successfully.</span><span>"</span><span>)</span>
        <span>return</span> <span>result</span>
    <span>return</span> <span>wrapper</span>

<span>@log_decorator</span>  <span># Applying decorator </span><span>def</span> <span>greet</span><span>():</span>
    <span>return</span> <span>"</span><span>Hello, World!</span><span>"</span>

<span>print</span><span>(</span><span>greet</span><span>())</span>
def log_decorator(func): def wrapper(): print(f"Calling function: {func.__name__}") result = func() print(f"Function {func.__name__} executed successfully.") return result return wrapper @log_decorator # Applying decorator def greet(): return "Hello, World!" print(greet())

Enter fullscreen mode Exit fullscreen mode

Output:

<span>Calling</span> <span>function</span><span>:</span> <span>greet</span>
<span>Function</span> <span>greet</span> <span>executed</span> <span>successfully</span><span>.</span>
<span>Hello</span><span>,</span> <span>World</span><span>!</span>
<span>Calling</span> <span>function</span><span>:</span> <span>greet</span>
<span>Function</span> <span>greet</span> <span>executed</span> <span>successfully</span><span>.</span>
<span>Hello</span><span>,</span> <span>World</span><span>!</span>
Calling function: greet Function greet executed successfully. Hello, World!

Enter fullscreen mode Exit fullscreen mode

What’s Happening?

The log_decorator wraps around greet(), adding logging before and after execution.
Using @log_decorator is the same as writing:
The log_decorator wraps around greet(), adding logging before and after execution.
Using @log_decorator is the same as writing:
The log_decorator wraps around greet(), adding logging before and after execution. Using @log_decorator is the same as writing:

Enter fullscreen mode Exit fullscreen mode

<span>greet</span> <span>=</span> <span>log_decorator</span><span>(</span><span>greet</span><span>)</span>
    <span>greet</span> <span>=</span> <span>log_decorator</span><span>(</span><span>greet</span><span>)</span>
greet = log_decorator(greet)

Enter fullscreen mode Exit fullscreen mode

2️⃣ Understanding the Inner Workings of Decorators

Decorators with Arguments

If a function takes arguments, the decorator must handle them properly.

<span>def</span> <span>log_decorator</span><span>(</span><span>func</span><span>):</span>
<span>def</span> <span>wrapper</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span> <span># Accept arguments </span> <span>print</span><span>(</span><span>f</span><span>"</span><span>Executing </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span> with arguments </span><span>{</span><span>args</span><span>}</span><span>, </span><span>{</span><span>kwargs</span><span>}</span><span>"</span><span>)</span>
<span>return</span> <span>func</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span>
<span>return</span> <span>wrapper</span>
<span>@log_decorator</span>
<span>def</span> <span>add</span><span>(</span><span>a</span><span>,</span> <span>b</span><span>):</span>
<span>return</span> <span>a</span> <span>+</span> <span>b</span>
<span>print</span><span>(</span><span>add</span><span>(</span><span>3</span><span>,</span> <span>5</span><span>))</span> <span># Logs the function execution </span>
<span>def</span> <span>log_decorator</span><span>(</span><span>func</span><span>):</span>
    <span>def</span> <span>wrapper</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span>  <span># Accept arguments </span>        <span>print</span><span>(</span><span>f</span><span>"</span><span>Executing </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span> with arguments </span><span>{</span><span>args</span><span>}</span><span>, </span><span>{</span><span>kwargs</span><span>}</span><span>"</span><span>)</span>
        <span>return</span> <span>func</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span>
    <span>return</span> <span>wrapper</span>

<span>@log_decorator</span>
<span>def</span> <span>add</span><span>(</span><span>a</span><span>,</span> <span>b</span><span>):</span>
    <span>return</span> <span>a</span> <span>+</span> <span>b</span>

<span>print</span><span>(</span><span>add</span><span>(</span><span>3</span><span>,</span> <span>5</span><span>))</span>  <span># Logs the function execution </span>
def log_decorator(func): def wrapper(*args, **kwargs): # Accept arguments print(f"Executing {func.__name__} with arguments {args}, {kwargs}") return func(*args, **kwargs) return wrapper @log_decorator def add(a, b): return a + b print(add(3, 5)) # Logs the function execution

Enter fullscreen mode Exit fullscreen mode

Output:

<span>Executing</span> <span>add</span> <span>with</span> <span>arguments </span><span>(</span><span>3</span><span>,</span> <span>5</span><span>),</span> <span>{}</span>
<span>8</span>
<span>Executing</span> <span>add</span> <span>with</span> <span>arguments </span><span>(</span><span>3</span><span>,</span> <span>5</span><span>),</span> <span>{}</span>
<span>8</span>
Executing add with arguments (3, 5), {} 8

Enter fullscreen mode Exit fullscreen mode

3️⃣ Applying Multiple Decorators

You can stack multiple decorators to modify a function in different ways.

<span>def</span> <span>uppercase_decorator</span><span>(</span><span>func</span><span>):</span>
<span>def</span> <span>wrapper</span><span>():</span>
<span>return</span> <span>func</span><span>().</span><span>upper</span><span>()</span>
<span>return</span> <span>wrapper</span>
<span>def</span> <span>exclamation_decorator</span><span>(</span><span>func</span><span>):</span>
<span>def</span> <span>wrapper</span><span>():</span>
<span>return</span> <span>func</span><span>()</span> <span>+</span> <span>"</span><span>!!!</span><span>"</span>
<span>return</span> <span>wrapper</span>
<span>@uppercase_decorator</span>
<span>@exclamation_decorator</span>
<span>def</span> <span>greet</span><span>():</span>
<span>return</span> <span>"</span><span>hello</span><span>"</span>
<span>print</span><span>(</span><span>greet</span><span>())</span> <span># Output: HELLO!!! </span>
<span>def</span> <span>uppercase_decorator</span><span>(</span><span>func</span><span>):</span>
    <span>def</span> <span>wrapper</span><span>():</span>
        <span>return</span> <span>func</span><span>().</span><span>upper</span><span>()</span>
    <span>return</span> <span>wrapper</span>

<span>def</span> <span>exclamation_decorator</span><span>(</span><span>func</span><span>):</span>
    <span>def</span> <span>wrapper</span><span>():</span>
        <span>return</span> <span>func</span><span>()</span> <span>+</span> <span>"</span><span>!!!</span><span>"</span>
    <span>return</span> <span>wrapper</span>

<span>@uppercase_decorator</span>
<span>@exclamation_decorator</span>
<span>def</span> <span>greet</span><span>():</span>
    <span>return</span> <span>"</span><span>hello</span><span>"</span>

<span>print</span><span>(</span><span>greet</span><span>())</span>  <span># Output: HELLO!!! </span>
def uppercase_decorator(func): def wrapper(): return func().upper() return wrapper def exclamation_decorator(func): def wrapper(): return func() + "!!!" return wrapper @uppercase_decorator @exclamation_decorator def greet(): return "hello" print(greet()) # Output: HELLO!!!

Enter fullscreen mode Exit fullscreen mode

Order Matters!

@exclamation_decorator adds "!!!".
@uppercase_decorator converts text to uppercase AFTER the exclamation marks are added.
@exclamation_decorator adds "!!!".
@uppercase_decorator converts text to uppercase AFTER the exclamation marks are added.
@exclamation_decorator adds "!!!". @uppercase_decorator converts text to uppercase AFTER the exclamation marks are added.

Enter fullscreen mode Exit fullscreen mode

4️⃣ Using Built-in Python Decorators

Python provides built-in decorators like @staticmethod, @classmethod, and @property.

Using @staticmethod and @classmethod in Classes

<span>class</span> <span>MathOperations</span><span>:</span>
<span>@staticmethod</span>
<span>def</span> <span>add</span><span>(</span><span>a</span><span>,</span> <span>b</span><span>):</span>
<span>return</span> <span>a</span> <span>+</span> <span>b</span>
<span>@classmethod</span>
<span>def</span> <span>class_method_example</span><span>(</span><span>cls</span><span>):</span>
<span>return</span> <span>f</span><span>"</span><span>This is a class method of </span><span>{</span><span>cls</span><span>.</span><span>__name__</span><span>}</span><span>"</span>
<span>print</span><span>(</span><span>MathOperations</span><span>.</span><span>add</span><span>(</span><span>3</span><span>,</span> <span>4</span><span>))</span> <span># Output: 7 </span><span>print</span><span>(</span><span>MathOperations</span><span>.</span><span>class_method_example</span><span>())</span> <span># Output: This is a class method of MathOperations </span>
<span>class</span> <span>MathOperations</span><span>:</span>
    <span>@staticmethod</span>
    <span>def</span> <span>add</span><span>(</span><span>a</span><span>,</span> <span>b</span><span>):</span>
        <span>return</span> <span>a</span> <span>+</span> <span>b</span>

    <span>@classmethod</span>
    <span>def</span> <span>class_method_example</span><span>(</span><span>cls</span><span>):</span>
        <span>return</span> <span>f</span><span>"</span><span>This is a class method of </span><span>{</span><span>cls</span><span>.</span><span>__name__</span><span>}</span><span>"</span>

<span>print</span><span>(</span><span>MathOperations</span><span>.</span><span>add</span><span>(</span><span>3</span><span>,</span> <span>4</span><span>))</span>  <span># Output: 7 </span><span>print</span><span>(</span><span>MathOperations</span><span>.</span><span>class_method_example</span><span>())</span>  <span># Output: This is a class method of MathOperations </span>
class MathOperations: @staticmethod def add(a, b): return a + b @classmethod def class_method_example(cls): return f"This is a class method of {cls.__name__}" print(MathOperations.add(3, 4)) # Output: 7 print(MathOperations.class_method_example()) # Output: This is a class method of MathOperations

Enter fullscreen mode Exit fullscreen mode

Key Differences:

@staticmethod: No self, doesn’t access instance attributes.
@classmethod: Uses cls, can modify class state.
@staticmethod: No self, doesn’t access instance attributes.
@classmethod: Uses cls, can modify class state.
@staticmethod: No self, doesn’t access instance attributes. @classmethod: Uses cls, can modify class state.

Enter fullscreen mode Exit fullscreen mode

Using @property for Getter and Setter Methods

<span>class</span> <span>Person</span><span>:</span>
<span>def</span> <span>__init__</span><span>(</span><span>self</span><span>,</span> <span>name</span><span>):</span>
<span>self</span><span>.</span><span>_name</span> <span>=</span> <span>name</span>
<span>@property</span>
<span>def</span> <span>name</span><span>(</span><span>self</span><span>):</span> <span># Getter </span> <span>return</span> <span>self</span><span>.</span><span>_name</span>
<span>@name.setter</span>
<span>def</span> <span>name</span><span>(</span><span>self</span><span>,</span> <span>new_name</span><span>):</span> <span># Setter </span> <span>if</span> <span>len</span><span>(</span><span>new_name</span><span>)</span> <span>></span> <span>2</span><span>:</span>
<span>self</span><span>.</span><span>_name</span> <span>=</span> <span>new_name</span>
<span>else</span><span>:</span>
<span>raise</span> <span>ValueError</span><span>(</span><span>"</span><span>Name too short!</span><span>"</span><span>)</span>
<span>p</span> <span>=</span> <span>Person</span><span>(</span><span>"</span><span>Alice</span><span>"</span><span>)</span>
<span>print</span><span>(</span><span>p</span><span>.</span><span>name</span><span>)</span> <span># Calls @property method </span><span>p</span><span>.</span><span>name</span> <span>=</span> <span>"</span><span>Bob</span><span>"</span> <span># Calls @name.setter method </span><span>print</span><span>(</span><span>p</span><span>.</span><span>name</span><span>)</span>
<span>class</span> <span>Person</span><span>:</span>
    <span>def</span> <span>__init__</span><span>(</span><span>self</span><span>,</span> <span>name</span><span>):</span>
        <span>self</span><span>.</span><span>_name</span> <span>=</span> <span>name</span>

    <span>@property</span>
    <span>def</span> <span>name</span><span>(</span><span>self</span><span>):</span>  <span># Getter </span>        <span>return</span> <span>self</span><span>.</span><span>_name</span>

    <span>@name.setter</span>
    <span>def</span> <span>name</span><span>(</span><span>self</span><span>,</span> <span>new_name</span><span>):</span>  <span># Setter </span>        <span>if</span> <span>len</span><span>(</span><span>new_name</span><span>)</span> <span>></span> <span>2</span><span>:</span>
            <span>self</span><span>.</span><span>_name</span> <span>=</span> <span>new_name</span>
        <span>else</span><span>:</span>
            <span>raise</span> <span>ValueError</span><span>(</span><span>"</span><span>Name too short!</span><span>"</span><span>)</span>

<span>p</span> <span>=</span> <span>Person</span><span>(</span><span>"</span><span>Alice</span><span>"</span><span>)</span>
<span>print</span><span>(</span><span>p</span><span>.</span><span>name</span><span>)</span>  <span># Calls @property method </span><span>p</span><span>.</span><span>name</span> <span>=</span> <span>"</span><span>Bob</span><span>"</span>  <span># Calls @name.setter method </span><span>print</span><span>(</span><span>p</span><span>.</span><span>name</span><span>)</span>  
class Person: def __init__(self, name): self._name = name @property def name(self): # Getter return self._name @name.setter def name(self, new_name): # Setter if len(new_name) > 2: self._name = new_name else: raise ValueError("Name too short!") p = Person("Alice") print(p.name) # Calls @property method p.name = "Bob" # Calls @name.setter method print(p.name)

Enter fullscreen mode Exit fullscreen mode

Why Use @property?

Avoids writing get_name() and set_name() methods.
Provides cleaner and more Pythonic code.
Avoids writing get_name() and set_name() methods.
Provides cleaner and more Pythonic code.
Avoids writing get_name() and set_name() methods. Provides cleaner and more Pythonic code.

Enter fullscreen mode Exit fullscreen mode

5️⃣ Real-World Use Cases for Decorators

1. Logging Function Execution

<span>import</span> <span>time</span>
<span>def</span> <span>timing_decorator</span><span>(</span><span>func</span><span>):</span>
<span>def</span> <span>wrapper</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span>
<span>start</span> <span>=</span> <span>time</span><span>.</span><span>time</span><span>()</span>
<span>result</span> <span>=</span> <span>func</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span>
<span>end</span> <span>=</span> <span>time</span><span>.</span><span>time</span><span>()</span>
<span>print</span><span>(</span><span>f</span><span>"</span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span> took </span><span>{</span><span>end</span> <span>-</span> <span>start</span><span>:</span><span>.</span><span>5</span><span>f</span><span>}</span><span> seconds to execute.</span><span>"</span><span>)</span>
<span>return</span> <span>result</span>
<span>return</span> <span>wrapper</span>
<span>@timing_decorator</span>
<span>def</span> <span>slow_function</span><span>():</span>
<span>time</span><span>.</span><span>sleep</span><span>(</span><span>2</span><span>)</span> <span># Simulate delay </span> <span>return</span> <span>"</span><span>Finished!</span><span>"</span>
<span>print</span><span>(</span><span>slow_function</span><span>())</span>
<span>import</span> <span>time</span>

<span>def</span> <span>timing_decorator</span><span>(</span><span>func</span><span>):</span>
    <span>def</span> <span>wrapper</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span>
        <span>start</span> <span>=</span> <span>time</span><span>.</span><span>time</span><span>()</span>
        <span>result</span> <span>=</span> <span>func</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span>
        <span>end</span> <span>=</span> <span>time</span><span>.</span><span>time</span><span>()</span>
        <span>print</span><span>(</span><span>f</span><span>"</span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span> took </span><span>{</span><span>end</span> <span>-</span> <span>start</span><span>:</span><span>.</span><span>5</span><span>f</span><span>}</span><span> seconds to execute.</span><span>"</span><span>)</span>
        <span>return</span> <span>result</span>
    <span>return</span> <span>wrapper</span>

<span>@timing_decorator</span>
<span>def</span> <span>slow_function</span><span>():</span>
    <span>time</span><span>.</span><span>sleep</span><span>(</span><span>2</span><span>)</span>  <span># Simulate delay </span>    <span>return</span> <span>"</span><span>Finished!</span><span>"</span>

<span>print</span><span>(</span><span>slow_function</span><span>())</span>
import time def timing_decorator(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.5f} seconds to execute.") return result return wrapper @timing_decorator def slow_function(): time.sleep(2) # Simulate delay return "Finished!" print(slow_function())

Enter fullscreen mode Exit fullscreen mode

Use Case: Performance monitoring for slow functions.

2. Authentication Check in APIs

<span>def</span> <span>authentication_required</span><span>(</span><span>func</span><span>):</span>
<span>def</span> <span>wrapper</span><span>(</span><span>user</span><span>):</span>
<span>if</span> <span>not</span> <span>user</span><span>.</span><span>get</span><span>(</span><span>"</span><span>is_authenticated</span><span>"</span><span>):</span>
<span>raise</span> <span>PermissionError</span><span>(</span><span>"</span><span>User not authenticated!</span><span>"</span><span>)</span>
<span>return</span> <span>func</span><span>(</span><span>user</span><span>)</span>
<span>return</span> <span>wrapper</span>
<span>@authentication_required</span>
<span>def</span> <span>get_user_data</span><span>(</span><span>user</span><span>):</span>
<span>return</span> <span>f</span><span>"</span><span>Welcome, </span><span>{</span><span>user</span><span>[</span><span>'</span><span>name</span><span>'</span><span>]</span><span>}</span><span>!</span><span>"</span>
<span>user</span> <span>=</span> <span>{</span><span>"</span><span>name</span><span>"</span><span>:</span> <span>"</span><span>Alice</span><span>"</span><span>,</span> <span>"</span><span>is_authenticated</span><span>"</span><span>:</span> <span>True</span><span>}</span>
<span>print</span><span>(</span><span>get_user_data</span><span>(</span><span>user</span><span>))</span> <span># Works </span>
<span>unauthenticated_user</span> <span>=</span> <span>{</span><span>"</span><span>name</span><span>"</span><span>:</span> <span>"</span><span>Bob</span><span>"</span><span>,</span> <span>"</span><span>is_authenticated</span><span>"</span><span>:</span> <span>False</span><span>}</span>
<span># print(get_user_data(unauthenticated_user)) # Raises PermissionError </span>
<span>def</span> <span>authentication_required</span><span>(</span><span>func</span><span>):</span>
    <span>def</span> <span>wrapper</span><span>(</span><span>user</span><span>):</span>
        <span>if</span> <span>not</span> <span>user</span><span>.</span><span>get</span><span>(</span><span>"</span><span>is_authenticated</span><span>"</span><span>):</span>
            <span>raise</span> <span>PermissionError</span><span>(</span><span>"</span><span>User not authenticated!</span><span>"</span><span>)</span>
        <span>return</span> <span>func</span><span>(</span><span>user</span><span>)</span>
    <span>return</span> <span>wrapper</span>

<span>@authentication_required</span>
<span>def</span> <span>get_user_data</span><span>(</span><span>user</span><span>):</span>
    <span>return</span> <span>f</span><span>"</span><span>Welcome, </span><span>{</span><span>user</span><span>[</span><span>'</span><span>name</span><span>'</span><span>]</span><span>}</span><span>!</span><span>"</span>

<span>user</span> <span>=</span> <span>{</span><span>"</span><span>name</span><span>"</span><span>:</span> <span>"</span><span>Alice</span><span>"</span><span>,</span> <span>"</span><span>is_authenticated</span><span>"</span><span>:</span> <span>True</span><span>}</span>
<span>print</span><span>(</span><span>get_user_data</span><span>(</span><span>user</span><span>))</span>  <span># Works </span>
<span>unauthenticated_user</span> <span>=</span> <span>{</span><span>"</span><span>name</span><span>"</span><span>:</span> <span>"</span><span>Bob</span><span>"</span><span>,</span> <span>"</span><span>is_authenticated</span><span>"</span><span>:</span> <span>False</span><span>}</span>
<span># print(get_user_data(unauthenticated_user)) # Raises PermissionError </span>
def authentication_required(func): def wrapper(user): if not user.get("is_authenticated"): raise PermissionError("User not authenticated!") return func(user) return wrapper @authentication_required def get_user_data(user): return f"Welcome, {user['name']}!" user = {"name": "Alice", "is_authenticated": True} print(get_user_data(user)) # Works unauthenticated_user = {"name": "Bob", "is_authenticated": False} # print(get_user_data(unauthenticated_user)) # Raises PermissionError

Enter fullscreen mode Exit fullscreen mode

Use Case: Restricting access to certain functions based on authentication.

3. Retry Mechanism for Failing Functions

<span>import</span> <span>time</span>
<span>import</span> <span>random</span>
<span>def</span> <span>retry_decorator</span><span>(</span><span>func</span><span>):</span>
<span>def</span> <span>wrapper</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span>
<span>for</span> <span>attempt</span> <span>in</span> <span>range</span><span>(</span><span>3</span><span>):</span> <span># Retry 3 times </span> <span>try</span><span>:</span>
<span>return</span> <span>func</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span>
<span>except</span> <span>Exception</span> <span>as</span> <span>e</span><span>:</span>
<span>print</span><span>(</span><span>f</span><span>"</span><span>Attempt </span><span>{</span><span>attempt</span><span>+</span><span>1</span><span>}</span><span> failed: </span><span>{</span><span>e</span><span>}</span><span>"</span><span>)</span>
<span>time</span><span>.</span><span>sleep</span><span>(</span><span>1</span><span>)</span>
<span>raise</span> <span>Exception</span><span>(</span><span>"</span><span>Function failed after 3 attempts!</span><span>"</span><span>)</span>
<span>return</span> <span>wrapper</span>
<span>@retry_decorator</span>
<span>def</span> <span>unstable_function</span><span>():</span>
<span>if</span> <span>random</span><span>.</span><span>random</span><span>()</span> <span><</span> <span>0.7</span><span>:</span> <span># 70% failure rate </span> <span>raise</span> <span>ValueError</span><span>(</span><span>"</span><span>Random failure occurred!</span><span>"</span><span>)</span>
<span>return</span> <span>"</span><span>Success!</span><span>"</span>
<span>print</span><span>(</span><span>unstable_function</span><span>())</span>
<span>import</span> <span>time</span>
<span>import</span> <span>random</span>

<span>def</span> <span>retry_decorator</span><span>(</span><span>func</span><span>):</span>
    <span>def</span> <span>wrapper</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span>
        <span>for</span> <span>attempt</span> <span>in</span> <span>range</span><span>(</span><span>3</span><span>):</span>  <span># Retry 3 times </span>            <span>try</span><span>:</span>
                <span>return</span> <span>func</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span>
            <span>except</span> <span>Exception</span> <span>as</span> <span>e</span><span>:</span>
                <span>print</span><span>(</span><span>f</span><span>"</span><span>Attempt </span><span>{</span><span>attempt</span><span>+</span><span>1</span><span>}</span><span> failed: </span><span>{</span><span>e</span><span>}</span><span>"</span><span>)</span>
                <span>time</span><span>.</span><span>sleep</span><span>(</span><span>1</span><span>)</span>
        <span>raise</span> <span>Exception</span><span>(</span><span>"</span><span>Function failed after 3 attempts!</span><span>"</span><span>)</span>
    <span>return</span> <span>wrapper</span>

<span>@retry_decorator</span>
<span>def</span> <span>unstable_function</span><span>():</span>
    <span>if</span> <span>random</span><span>.</span><span>random</span><span>()</span> <span><</span> <span>0.7</span><span>:</span>  <span># 70% failure rate </span>        <span>raise</span> <span>ValueError</span><span>(</span><span>"</span><span>Random failure occurred!</span><span>"</span><span>)</span>
    <span>return</span> <span>"</span><span>Success!</span><span>"</span>

<span>print</span><span>(</span><span>unstable_function</span><span>())</span>
import time import random def retry_decorator(func): def wrapper(*args, **kwargs): for attempt in range(3): # Retry 3 times try: return func(*args, **kwargs) except Exception as e: print(f"Attempt {attempt+1} failed: {e}") time.sleep(1) raise Exception("Function failed after 3 attempts!") return wrapper @retry_decorator def unstable_function(): if random.random() < 0.7: # 70% failure rate raise ValueError("Random failure occurred!") return "Success!" print(unstable_function())

Enter fullscreen mode Exit fullscreen mode

Use Case: Handling unreliable operations like API calls, database queries, and network requests.

6️⃣ Best Practices for Using Decorators

Use functools.wraps(func) inside decorators to preserve function metadata.

<span>from</span> <span>functools</span> <span>import</span> <span>wraps</span>
<span>def</span> <span>log_decorator</span><span>(</span><span>func</span><span>):</span>
<span>@wraps</span><span>(</span><span>func</span><span>)</span> <span># Preserves original function name and docstring </span> <span>def</span> <span>wrapper</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span>
<span>print</span><span>(</span><span>f</span><span>"</span><span>Executing </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span>"</span><span>)</span>
<span>return</span> <span>func</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span>
<span>return</span> <span>wrapper</span>
<span>from</span> <span>functools</span> <span>import</span> <span>wraps</span>

<span>def</span> <span>log_decorator</span><span>(</span><span>func</span><span>):</span>
    <span>@wraps</span><span>(</span><span>func</span><span>)</span>  <span># Preserves original function name and docstring </span>    <span>def</span> <span>wrapper</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span>
        <span>print</span><span>(</span><span>f</span><span>"</span><span>Executing </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span>"</span><span>)</span>
        <span>return</span> <span>func</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span>
    <span>return</span> <span>wrapper</span>
from functools import wraps def log_decorator(func): @wraps(func) # Preserves original function name and docstring def wrapper(*args, **kwargs): print(f"Executing {func.__name__}") return func(*args, **kwargs) return wrapper

Enter fullscreen mode Exit fullscreen mode

Use decorators only when necessary to keep code readable.

Stack decorators carefully as order matters.

Conclusion

Decorators modify functions without changing their core logic.

Built-in decorators like @staticmethod and @property improve class functionality.

Use decorators for logging, authentication, and performance monitoring.

Understanding decorators makes your code more reusable and efficient!

What’s Next?

In the next post, we’ll explore Metaclasses in Python – The Power Behind Class Creation. Stay tuned!

What Do You Think?

Have you used decorators in your projects? What’s your favorite use case? Let’s discuss in the comments!

原文链接:Python Decorators – Enhancing Functions with Powerful Wrappers

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
Never give up your dreams. Miracles happen everyday.
别放弃梦想,奇迹每天都在上演
评论 抢沙发

请登录后发表评论

    暂无评论内容