Fundamentals 17 min read

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.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Master Python Decorators: From Basics to Advanced Techniques

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 name

Using 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 wrapper

Now 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._x

Similar 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):
        pass

Common 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 wrapper

Even 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.py

lets 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)
wrapt

provides 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):
    pass

Using 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonloggingDecoratorfunctoolswrapt
MaGe Linux Operations
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.