Mastering Python Decorators: Enhance Functions with Expert Techniques
Python Decorators
Decorators let you wrap functions with reusable logic, making your code more modular, readable, and maintainable. In this guide we build, explain, and apply decorators in real‑world scenarios.
Video: @Decorators in Python
Decorators in Python
Python’s decorators are a powerful metaprogramming tool that allows you to add behavior to functions or classes at definition time. Because everything in Python is an object—including functions and classes—decorators operate by taking one callable, wrapping it in another, and returning the new callable.
Prerequisites for Learning Decorators
Before diving in, you should be comfortable with the fact that in Python, functions are first‑class objects. Names are simply references bound to objects; the same function can be referenced by multiple names.
def first(msg):
print(msg)
first("Hello")
second = first
second("Hello")
Output
Hello Hello
Functions can be passed as arguments, returned from other functions, and even nested within other functions. These concepts—higher‑order functions, nested functions, and closures—are the building blocks of decorators.
def inc(x):
return x + 1
def dec(x):
return x - 1
def operate(func, x):
result = func(x)
return result
Usage:
>>>>> operate(inc,3)
4
>>> operate(dec,3)
2
def is_called():
def is_returned():
print("Hello")
return is_returned
new = is_called()
new()
Output
Hello
Understanding closures is key: the nested function retains access to the enclosing scope, which is precisely what decorators exploit to store metadata or modify behavior.
Getting Back to Decorators
In Python, a decorator is simply a callable that accepts a function (or another callable) and returns a new callable. This returned function typically wraps the original, adding or altering functionality before or after the call.
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
Example usage:
>>>>> ordinary()
I am ordinary
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary
The decorator pattern keeps the original function’s semantics intact while augmenting it. To avoid manual reassignment, Python provides syntactic sugar with the @ symbol:
@make_pretty
def ordinary():
print("I am ordinary")
This is equivalent to:
def ordinary():
print("I am ordinary")
ordinary = make_pretty(ordinary)
Decorating Functions with Parameters
Many decorators must handle arbitrary arguments. The *args and **kwargs syntax lets you capture any positional or keyword arguments and forward them unchanged.
def works_for_all(func):
def inner(*args, **kwargs):
print("I can decorate any function")
return func(*args, **kwargs)
return inner
Handling edge cases is another common decorator use‑case. For instance, guarding against division by zero:
def smart_divide(func):
def inner(a, b):
print("I am going to divide", a, "and", b)
if b == 0:
print("Whoops! cannot divide")
return
return func(a, b)
return inner
@smart_divide
def divide(a, b):
print(a/b)
Usage:
>>>>> divide(2,5)
I am going to divide 2 and 5
0.4
>>> divide(2,0)
I am going to divide 2 and 0
Whoops! cannot divide
Chaining Decorators in Python
Decorators can be stacked. The order of application matters, as each decorator receives the function returned by the one below it.
def star(func):
def inner(*args, **kwargs):
print("*" * 30)
func(*args, **kwargs)
print("*" * 30)
return inner
def percent(func):
def inner(*args, **kwargs):
print("%" * 30)
func(*args, **kwargs)
print("%" * 30)
return inner
@star
@percent
def printer(msg):
print(msg)
Result:
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
Reversing the order swaps the surrounding patterns, demonstrating how decorator stacking influences behavior.
Python
- Master Python Functions: Syntax, Types, and Practical Examples
- Mastering Python Function Arguments: Positional, Keyword, and Default Parameters
- Mastering Python Recursion: How Functions Call Themselves
- Python Lambda Functions: A Practical Guide to Anonymous Functions
- Python Generators: Efficient Iteration and Advanced Use Cases
- Python Closures Explained: How Nested Functions Capture Variables
- Mastering Python’s strip() Method: Comprehensive Guide & Practical Examples
- Python round() Function Explained with Practical Examples
- Mastering Python's map() Function: Syntax, Examples, and Best Practices
- Python Functions Explained: Building Reusable Code Modules