Master Python Decorators: From Basics to Advanced Techniques
This comprehensive guide explains why Python decorators are essential, walks through simple and advanced examples—including logging, parameterized, and class-based decorators—covers common pitfalls, and shows how to use tools like functools, decorator.py, and wrapt for clean, reusable code.
Decorators are a crucial Python concept that often serve as a gateway to more advanced programming.
Why Do We Need Decorators?
Suppose your program defines say_hello() and say_goodbye(). When both functions are called, the output shows two "hello" messages because say_goodbye() contains a bug.
The boss requires logging the entry time and name of each function before it runs.
One naive solution is to call a debug() function at the start of every business function, but this quickly becomes cumbersome, especially when only certain functions need debugging.
In essence, a decorator is a Python function that can add extra functionality to other functions without modifying their code; it returns a function object and is commonly used for logging, performance testing, transactions, caching, and permission checks.
The core purpose of a decorator is to add additional functionality to an existing function or object .
How to Write a Simple Decorator
Before Python 2.4, you would manually wrap a function:
def debug(func):
def wrapper():
print "[DEBUG]: enter {}()".format(func.__name__)
return func()
return wrapper
def say_hello():
print "hello!"
say_hello = debug(say_hello) # add functionality without changing the original nameUsing the @ syntax makes this cleaner:
def debug(func):
def wrapper():
print "[DEBUG]: enter {}()".format(func.__name__)
return func()
return wrapper
@debug
def say_hello():
print "hello!"This basic decorator fails when the wrapped function requires arguments.
def debug(func):
def wrapper(*args, **kwargs):
print "[DEBUG]: enter {}()".format(func.__name__)
return func(*args, **kwargs)
return wrapperNow the decorator works with any signature using *args and **kwargs.
Parameterized Decorators
Sometimes you need the decorator itself to accept arguments, such as a log level:
def logging(level):
def wrapper(func):
def inner_wrapper(*args, **kwargs):
print "[{level}]: enter function {func}()".format(level=level, func=func.__name__)
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@logging(level='INFO')
def say(something):
print "say {}!".format(something)Class‑Based Decorators
A decorator can also be implemented as a class that implements __call__:
class logging(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print "[DEBUG]: enter function {func}()".format(func=self.func.__name__)
return self.func(*args, **kwargs)
@logging
def say(something):
print "say {}!".format(something)For a parameterized class decorator, store the parameters in __init__ and return a wrapper function from __call__:
class logging(object):
def __init__(self, level='INFO'):
self.level = level
def __call__(self, func):
def wrapper(*args, **kwargs):
print "[{level}]: enter function {func}()".format(level=self.level, func=func.__name__)
return func(*args, **kwargs)
return wrapper
@logging(level='INFO')
def say(something):
print "say {}!".format(something)Built‑in Decorators
Python’s built‑in decorators work the same way but return class objects instead of plain functions.
@property
Before @property, a getter, setter, and deleter had to be created manually using the property() function. With @property the syntax is simplified:
@property
def x(self):
return self._xSimilar decorators exist for @setter, @getter, and @deleter.
@classmethod and @staticmethod
Both return special descriptor objects. The order matters: place your custom decorator before @staticmethod or @classmethod to avoid attribute errors.
class Car(object):
@staticmethod
@logging
def check_model_for(obj):
passCommon Pitfalls
Adding logic outside the wrapper can change execution order unexpectedly. Also, without functools.wraps, the decorated function loses its original name, docstring, and metadata.
from functools import wraps
def logging(func):
@wraps(func)
def wrapper(*args, **kwargs):
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)
return wrapperEven with wraps, the signature and source code may still be hidden; third‑party libraries like wrapt or decorator can preserve them fully.
Optimizing Decorators with Third‑Party Tools
decorator.pylets you define a wrapper function and apply it via decorate(func, wrapper), or use its @decorator shortcut.
from decorator import decorate
def wrapper(func, *args, **kwargs):
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)
def logging(func):
return decorate(func, wrapper) wraptprovides a robust decorator API that preserves metadata, works with instance methods, class methods, and static methods, and handles arguments consistently:
import wrapt
@wrapt.decorator
def logging(wrapped, instance, args, kwargs):
print "[DEBUG]: enter {}()".format(wrapped.__name__)
return wrapped(*args, **kwargs)
@logging
def say(something):
passUsing wrapt for parameterized decorators follows the same pattern, nesting the outer function that captures parameters.
Conclusion
Python decorators differ from Java annotations or C# attributes; they wrap functions or objects to extend behavior at call time, enabling logging, caching, and many other cross‑cutting concerns.
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.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
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.
