Understanding Python Decorators: A Beginner’s Guide with Examples
Python decorators are a powerful and versatile tool for modifying the behavior of functions or methods. They allow you to add functionality to existing code without altering its structure. In this article, we’ll break down decorators and provide simple examples to help you understand and use them effectively.
What are Decorators?
A decorator in Python is essentially a function that takes another function as an argument and extends or alters its behavior. Decorators are commonly used to add features like logging, access control, memoization, or validation to existing functions or methods.
Decorators in Python are applied using the @decorator_name
syntax placed above the function definition.
Anatomy of a Decorator
A basic decorator function has the following structure:
<span>def</span> <span>decorator_function</span><span>(</span><span>original_function</span><span>):</span><span>def</span> <span>wrapper_function</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span><span># Code to execute before the original function </span> <span>result</span> <span>=</span> <span>original_function</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span><span># Code to execute after the original function </span> <span>return</span> <span>result</span><span>return</span> <span>wrapper_function</span><span>def</span> <span>decorator_function</span><span>(</span><span>original_function</span><span>):</span> <span>def</span> <span>wrapper_function</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>):</span> <span># Code to execute before the original function </span> <span>result</span> <span>=</span> <span>original_function</span><span>(</span><span>*</span><span>args</span><span>,</span> <span>**</span><span>kwargs</span><span>)</span> <span># Code to execute after the original function </span> <span>return</span> <span>result</span> <span>return</span> <span>wrapper_function</span>def decorator_function(original_function): def wrapper_function(*args, **kwargs): # Code to execute before the original function result = original_function(*args, **kwargs) # Code to execute after the original function return result return wrapper_function
Enter fullscreen mode Exit fullscreen mode
Applying a Decorator
You can apply a decorator to a function using the @decorator_name
syntax or manually:
<span>@decorator_function</span><span>def</span> <span>some_function</span><span>():</span><span>print</span><span>(</span><span>"</span><span>This is the original function.</span><span>"</span><span>)</span><span># Equivalent to: # some_function = decorator_function(some_function) </span><span>@decorator_function</span> <span>def</span> <span>some_function</span><span>():</span> <span>print</span><span>(</span><span>"</span><span>This is the original function.</span><span>"</span><span>)</span> <span># Equivalent to: # some_function = decorator_function(some_function) </span>@decorator_function def some_function(): print("This is the original function.") # Equivalent to: # some_function = decorator_function(some_function)
Enter fullscreen mode Exit fullscreen mode
Example 1: A Basic Decorator
Let’s create a simple decorator that prints a message before and after a function runs.
<span>def</span> <span>simple_decorator</span><span>(</span><span>func</span><span>):</span><span>def</span> <span>wrapper</span><span>():</span><span>print</span><span>(</span><span>"</span><span>Before the function call.</span><span>"</span><span>)</span><span>func</span><span>()</span><span>print</span><span>(</span><span>"</span><span>After the function call.</span><span>"</span><span>)</span><span>return</span> <span>wrapper</span><span>@simple_decorator</span><span>def</span> <span>say_hello</span><span>():</span><span>print</span><span>(</span><span>"</span><span>Hello, World!</span><span>"</span><span>)</span><span>say_hello</span><span>()</span><span>def</span> <span>simple_decorator</span><span>(</span><span>func</span><span>):</span> <span>def</span> <span>wrapper</span><span>():</span> <span>print</span><span>(</span><span>"</span><span>Before the function call.</span><span>"</span><span>)</span> <span>func</span><span>()</span> <span>print</span><span>(</span><span>"</span><span>After the function call.</span><span>"</span><span>)</span> <span>return</span> <span>wrapper</span> <span>@simple_decorator</span> <span>def</span> <span>say_hello</span><span>():</span> <span>print</span><span>(</span><span>"</span><span>Hello, World!</span><span>"</span><span>)</span> <span>say_hello</span><span>()</span>def simple_decorator(func): def wrapper(): print("Before the function call.") func() print("After the function call.") return wrapper @simple_decorator def say_hello(): print("Hello, World!") say_hello()
Enter fullscreen mode Exit fullscreen mode
Output:
Before the function call.Hello, World!After the function call.Before the function call. Hello, World! After the function call.Before the function call. Hello, World! After the function call.
Enter fullscreen mode Exit fullscreen mode
Example 2: A Decorator with Arguments
You can create a decorator that accepts arguments by wrapping it in another function.
<span>def</span> <span>repeat_decorator</span><span>(</span><span>times</span><span>):</span><span>def</span> <span>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>_</span> <span>in</span> <span>range</span><span>(</span><span>times</span><span>):</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>return</span> <span>decorator</span><span>@repeat_decorator</span><span>(</span><span>3</span><span>)</span><span>def</span> <span>greet</span><span>(</span><span>name</span><span>):</span><span>print</span><span>(</span><span>f</span><span>"</span><span>Hello, </span><span>{</span><span>name</span><span>}</span><span>!</span><span>"</span><span>)</span><span>greet</span><span>(</span><span>"</span><span>Alice</span><span>"</span><span>)</span><span>def</span> <span>repeat_decorator</span><span>(</span><span>times</span><span>):</span> <span>def</span> <span>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>_</span> <span>in</span> <span>range</span><span>(</span><span>times</span><span>):</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>return</span> <span>decorator</span> <span>@repeat_decorator</span><span>(</span><span>3</span><span>)</span> <span>def</span> <span>greet</span><span>(</span><span>name</span><span>):</span> <span>print</span><span>(</span><span>f</span><span>"</span><span>Hello, </span><span>{</span><span>name</span><span>}</span><span>!</span><span>"</span><span>)</span> <span>greet</span><span>(</span><span>"</span><span>Alice</span><span>"</span><span>)</span>def repeat_decorator(times): def decorator(func): def wrapper(*args, **kwargs): for _ in range(times): func(*args, **kwargs) return wrapper return decorator @repeat_decorator(3) def greet(name): print(f"Hello, {name}!") greet("Alice")
Enter fullscreen mode Exit fullscreen mode
Output:
Hello, Alice!Hello, Alice!Hello, Alice!Hello, Alice! Hello, Alice! Hello, Alice!Hello, Alice! Hello, Alice! Hello, Alice!
Enter fullscreen mode Exit fullscreen mode
Real-Life Applications of Decorators
Decorators are extensively used in real-world scenarios. Here are some simplified practical examples:
1. Logging User Actions
You can use a decorator to log every time a user performs an action.
<span>def</span> <span>log_action</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>print</span><span>(</span><span>f</span><span>"</span><span>Action: </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span> is being performed.</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_action</span><span>def</span> <span>upload_file</span><span>(</span><span>filename</span><span>):</span><span>print</span><span>(</span><span>f</span><span>"</span><span>Uploading </span><span>{</span><span>filename</span><span>}</span><span>...</span><span>"</span><span>)</span><span>upload_file</span><span>(</span><span>"</span><span>report.pdf</span><span>"</span><span>)</span><span>def</span> <span>log_action</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>print</span><span>(</span><span>f</span><span>"</span><span>Action: </span><span>{</span><span>func</span><span>.</span><span>__name__</span><span>}</span><span> is being performed.</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_action</span> <span>def</span> <span>upload_file</span><span>(</span><span>filename</span><span>):</span> <span>print</span><span>(</span><span>f</span><span>"</span><span>Uploading </span><span>{</span><span>filename</span><span>}</span><span>...</span><span>"</span><span>)</span> <span>upload_file</span><span>(</span><span>"</span><span>report.pdf</span><span>"</span><span>)</span>def log_action(func): def wrapper(*args, **kwargs): print(f"Action: {func.__name__} is being performed.") return func(*args, **kwargs) return wrapper @log_action def upload_file(filename): print(f"Uploading {filename}...") upload_file("report.pdf")
Enter fullscreen mode Exit fullscreen mode
Output:
Action: upload_file is being performed.Uploading report.pdf...Action: upload_file is being performed. Uploading report.pdf...Action: upload_file is being performed. Uploading report.pdf...
Enter fullscreen mode Exit fullscreen mode
2. Tracking Execution Time
Track how long tasks take to execute, useful for performance monitoring.
<span>import</span> <span>time</span><span>def</span> <span>track_time</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>2</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>@track_time</span><span>def</span> <span>download_file</span><span>(</span><span>file_size</span><span>):</span><span>time</span><span>.</span><span>sleep</span><span>(</span><span>file_size</span> <span>/</span> <span>10</span><span>)</span> <span># Simulate download time </span> <span>print</span><span>(</span><span>"</span><span>Download complete.</span><span>"</span><span>)</span><span>download_file</span><span>(</span><span>50</span><span>)</span><span>import</span> <span>time</span> <span>def</span> <span>track_time</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>2</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>@track_time</span> <span>def</span> <span>download_file</span><span>(</span><span>file_size</span><span>):</span> <span>time</span><span>.</span><span>sleep</span><span>(</span><span>file_size</span> <span>/</span> <span>10</span><span>)</span> <span># Simulate download time </span> <span>print</span><span>(</span><span>"</span><span>Download complete.</span><span>"</span><span>)</span> <span>download_file</span><span>(</span><span>50</span><span>)</span>import time def track_time(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.2f} seconds to execute.") return result return wrapper @track_time def download_file(file_size): time.sleep(file_size / 10) # Simulate download time print("Download complete.") download_file(50)
Enter fullscreen mode Exit fullscreen mode
Output:
Download complete.download_file took 5.00 seconds to execute.Download complete. download_file took 5.00 seconds to execute.Download complete. download_file took 5.00 seconds to execute.
Enter fullscreen mode Exit fullscreen mode
3. Adding User Greetings
A decorator can personalize greetings by adding dynamic elements.
<span>def</span> <span>add_greeting</span><span>(</span><span>func</span><span>):</span><span>def</span> <span>wrapper</span><span>(</span><span>name</span><span>):</span><span>print</span><span>(</span><span>"</span><span>Hello, welcome!</span><span>"</span><span>)</span><span>func</span><span>(</span><span>name</span><span>)</span><span>return</span> <span>wrapper</span><span>@add_greeting</span><span>def</span> <span>show_user_profile</span><span>(</span><span>name</span><span>):</span><span>print</span><span>(</span><span>f</span><span>"</span><span>User Profile: </span><span>{</span><span>name</span><span>}</span><span>"</span><span>)</span><span>show_user_profile</span><span>(</span><span>"</span><span>Alice</span><span>"</span><span>)</span><span>def</span> <span>add_greeting</span><span>(</span><span>func</span><span>):</span> <span>def</span> <span>wrapper</span><span>(</span><span>name</span><span>):</span> <span>print</span><span>(</span><span>"</span><span>Hello, welcome!</span><span>"</span><span>)</span> <span>func</span><span>(</span><span>name</span><span>)</span> <span>return</span> <span>wrapper</span> <span>@add_greeting</span> <span>def</span> <span>show_user_profile</span><span>(</span><span>name</span><span>):</span> <span>print</span><span>(</span><span>f</span><span>"</span><span>User Profile: </span><span>{</span><span>name</span><span>}</span><span>"</span><span>)</span> <span>show_user_profile</span><span>(</span><span>"</span><span>Alice</span><span>"</span><span>)</span>def add_greeting(func): def wrapper(name): print("Hello, welcome!") func(name) return wrapper @add_greeting def show_user_profile(name): print(f"User Profile: {name}") show_user_profile("Alice")
Enter fullscreen mode Exit fullscreen mode
Output:
Hello, welcome!User Profile: AliceHello, welcome! User Profile: AliceHello, welcome! User Profile: Alice
Enter fullscreen mode Exit fullscreen mode
Key Takeaways
- Decorators are a powerful way to modify the behavior of functions or methods.
- They can simplify repetitive tasks like logging, timing, or personalization.
- Use the
@decorator
syntax to apply them conveniently. - Decorators can accept arguments and be nested for added flexibility.
By mastering decorators, you’ll unlock a valuable tool for writing clean and efficient Python code. Start experimenting with the examples provided to get comfortable with this concept!
原文链接:Understanding Python Decorators: A Beginner’s Guide with Examples
暂无评论内容