Unlock Python Power: Master Decorators in Minutes
This article explains Python decorators—from basic analogies and the underlying concepts of higher‑order functions and closures to practical examples like timing and logging decorators—showing how to enhance functions dynamically without altering their source code while preserving metadata with functools.wraps.
Python developers often need to add features such as logging, timing, or permission checks to a function without touching its internal code. Decorators provide an elegant, powerful way to achieve this.
1. What is a decorator? A simple analogy
Imagine a core function (e.g., "deliver a package") that is pure. Before the delivery you might make a phone call, and after delivery you might add a signature. A decorator works like a "package wrapper" that adds these steps before and after the core function.
It takes your core function.
Adds new behavior (e.g., a phone call and a signature).
Returns a brand‑new, enhanced function.
2. Core foundation: Understanding closures and higher‑order functions
1. Higher‑order function
A function can be passed as an argument to another function and can also be returned as a value. This is the skeleton of a decorator.
def outer_func(func):
def inner_func():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return inner_func
def say_hello():
print("Hello!")
enhanced_say_hello = outer_func(say_hello)
enhanced_say_hello()2. Closure
The inner function remembers and accesses the outer function’s local variables (e.g., func) even after the outer function has finished executing. This is the soul of a decorator.
3. Magic moment: @ syntax sugar
Python’s @ symbol is a convenient syntactic sugar that makes applying decorators extremely simple.
def outer_func(func):
def inner_func():
print("Before calling...")
func()
print("After calling...")
return inner_func
@outer_func
def say_hello():
print("Hello!")
say_hello() # actually calls the decorated versionThe line @outer_func is equivalent to say_hello = outer_func(say_hello).
4. Practical: Writing useful decorators
Case 1: Timer decorator
import time
def timer(func):
"""Calculate the execution time of a function"""
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
run_time = end_time - start_time
print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
return result
return wrapper
@timer
def long_running_function(n):
"""Simulate a time‑consuming function"""
for i in range(n):
sum([j**2 for j in range(10000)])
return "Done"
result = long_running_function(10)
print(result)Case 2: Logging decorator
def logger(func):
"""Log function call information"""
def wrapper(*args, **kwargs):
print(f"[INFO] Calling function: {func.__name__}")
print(f"[INFO] Arguments: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"[INFO] Function {func.__name__} finished.")
return result
return wrapper
@logger
def add_numbers(a, b):
return a + b
print(add_numbers(3, b=4))5. Solving a side effect of decorators
After decoration, the original function’s metadata (e.g., __name__, __doc__) is replaced by the wrapper’s metadata. Use functools.wraps to preserve the original information.
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print(f"Finished {func.__name__!r} in {end_time - start_time:.4f} secs")
return result
return wrapper
@timer
def long_running_function(n):
...
print(long_running_function.__name__) # correctly prints 'long_running_function'6. Summary and key points
Essence: a higher‑order function that returns a function, leveraging closures.
Purpose: dynamically enhance a function without modifying its code, following the open‑closed principle.
Core: the @ syntax is merely syntactic sugar for func = decorator(func).
Best practice: always use functools.wraps to retain the original function’s metadata.
Advantages: decoupling, code reuse, and flexible composition (multiple decorators can be stacked).
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
