Understanding Python Decorators: Concepts, Examples, and Execution Order
This article explains Python decorators by first introducing the concept of closures, then demonstrating basic and advanced decorator implementations—including logging, timing, class-based decorators, and the use of functools.wraps—while also covering decorator execution order and providing annotated code examples.
In Python, adding new functionality to an object can be done by directly extending its class, using composition, or employing inheritance; composition is generally preferred, while decorators represent a fourth, dynamic approach that modifies objects at runtime.
Typical decorator scenarios include logging, performance testing, transaction handling, and permission verification.
Understanding decorators requires grasping closures: a nested function that accesses variables from its enclosing scope and is returned as a callable. Decorators are essentially closures that receive a function as an argument.
Simple closure example:
def add_num(x):
def sum_num(y):
return x + y
return sum_num
add_num5 = add_num(5)
total_num = add_num5(100)
print(total_num)Basic timing decorator example:
def times_use(func):
def count_times(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(end - start)
return result
return count_times
@times_use
def test_decorator():
time.sleep(2)
print("Test Decorator")
test_decorator()Class‑based decorator that optionally logs execution time:
class logTime:
def __init__(self, use_log=False):
self._use_log = use_log
def __call__(self, func):
def _log(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
print(result)
end_time = time.time()
if self._use_log:
print(end_time - start_time)
return result
return _log
@logTime(True)
def test_decorator():
time.sleep(2)
print("Test Decorator")
return "Success"
test_decorator()When a decorator needs to preserve the original function’s metadata (__name__, __doc__, etc.), functools.wraps should be applied:
from functools import wraps
def use_logging(level):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
result = func(*args, **kwargs)
print(result)
return result
return wrapper
return decorator
@use_logging("warn")
def test_decorator():
"""Test Decorator DocString"""
time.sleep(2)
print("Test Decorator")
return "Success"
print(test_decorator.__name__)
print(test_decorator.__doc__)Multiple decorators are executed in two phases: during function definition they are applied from the innermost to the outermost (inner‑to‑outer), while during function call they run from outermost to innermost (outer‑to‑inner). Example:
def decorator_a(func):
print("Get in Decorator_a")
def inner_a(*args, **kwargs):
print("Get in Inner_a")
return func(*args, **kwargs)
return inner_a
def decorator_b(func):
print("Get in Decorator_b")
def inner_b(*args, **kwargs):
print("Get in Inner_b")
return func(*args, **kwargs)
return inner_b
@decorator_b
@decorator_a
def test_decorator():
"""test decorator DocString"""
print("Test Decorator")
return "Success"
test_decorator()Running the above yields:
Get in Decorator_a
Get in Decorator_b
Get in Inner_b
Get in Inner_a
Test DecoratorThe output demonstrates that decorators are applied inside‑out at definition time and executed outside‑in at call time, allowing layered functionality without modifying the original function.
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.