Python: Decorator

By Xah Lee. Date: . Last updated: .

What is decorator

Python “decorator” is a language construct that lets you define a wrapper function g to another function f, such that, when f is called, your wrapper g is called instead. (it can also be applied to methods, and Class .)

The syntax for “decorator” is @name1 immediately before the line of def name2.

# suppose you defined a function my_wrapper.

# now,
# this syntax with the at sign @

@my_wrapper
def f():
    # whatever body
    print("something")

# is equivalent to

def f():
    # whatever body
    print("something")

f = my_wrapper(f)

# note, to use the @my_wrapper syntax, you must place it right above the definition of another function f.
# In other words, you must have access to the source code of f and be able to modify it.

# if you don't have access to the source code of f you want to modify,
# just do
# f = my_wrapper(f)

# s------------------------------

# Note that:
# when the function f is called, the wrapper g will be called instead.
# The wrapper function g receives f as its argument.
# The wrapper function g must return a function.
# Because the value of f is now g(f), and f is a function.
# So, when f is called such as f(3), Python evaluates g(f)(3), so g(f) must be a function too. (technically, it just need to be anything

Example: Check or Modify Argument

Here's a example of decorator. It checks and modifies the function's argument.

# example of a decorator, without using the @ syntax


def gg(xfun):
    """A decorator for function ff. Make sure input to ff is always even. If not, add 1 to make it even."""

    def hh(y):
        if y % 2 == 0:  # even
            return xfun(y)
        else:
            return xfun(y + 1)

    return hh

def ff(x):
    return x

ff = gg(ff)

print(ff(3))  # 4
print(ff(4))  # 4
print(ff(5))  # 6
print(ff(6))  # 6

now the same example but using the decorator syntax:

# example of a decorator


def gg(xfun):
    """A decorator for function ff. Make sure input to ff is always even. If not, add 1 to make it even."""

    def hh(y):
        if y % 2 == 0:  # even
            return xfun(y)
        else:
            return xfun(y + 1)

    return hh

@gg
def ff(x):
    return x

print(ff(3))  # 4
print(ff(4))  # 4
print(ff(5))  # 6
print(ff(6))  # 6

Checklist for writing a decorator

Here's 4 things to remember when writing a decorator:

  1. The wrapper function g must take a function as argument. (because it'll receive f as argument.)
  2. The wrapper function g must return a function (e.g. h). (because calling f results in calling return value of g.)
  3. The returned function h must take the same number and type of arguments as f. (Tip: use def h(*args, **keywords) to catch all.)
  4. OFTEN, the returned function h should return the same type of value that f returns. If f returns a string, h probably should too. If f returns None, h probably should too.

Example: Catch All Arguments (And Run Conditionally)

When defining the decorator function, often you need to catch all possible arguments of the original function. Here's a example of how.

In the following example, the original function is called only if some global variable is true.

# example of decorator that check condition to decide whether to call

# the condition
cc = True

def gg(func):
    """a decorator for ff. If cc is true, run ff, else do nothing"""

    def hh(*args, **xkeywords):  # param must catch all args of ff
        """do nothing function"""
        pass

    if cc:
        return func
    else:
        return hh

@gg
def ff(*args, **xkeywords):
    # ff can be sending email, or any callable with no return value
    print("ff")
    pass

ff(3)
ff(3, "Jane")
ff(3, 4, k="thank you")

# if cc is true, then ff is called 3 times
# (different set of arguments are used, to illustrate that our wrapper work well with them)

# if cc is false, Nothing's done, ff is not called.

[see Python: Function]

Decorator with parameters

Decorator itself can have parameters.

For example, the following decorator:

@g(3)
def f():
    print("xyz")

# is equivalent to

def f():
    print("xyz")

f = g(3)(f)

# There's nothing special about this.
# In the simplest decorator example, @g followed by def f: gets transformed into f = g(f).
# Now, if we replace g by g(x), we get f = g(x)(f).

Decorators can get complex, because it's transforming code and doing function applications and involves nested functions. In this case, just remember that:

g(x) must return a function, this function will receive f as argument. thus we can see, g(x) must return a decorator.

Example of decorator with parameter

# example of decorator with argument

def gg(num):
    """a decorator for ff. Ignore ff. Simply return num."""

    def h1(f1):
        def h2(f2):
            return num

        return h2

    return h1

# gg(1) must return a function h1, such that h1 accept function (the ff), and also return a function (to be applied to ff's args)
@gg(1)
def ff(x):
    return repr(x) + " rabbits"

print(ff(3))  # 1
print(ff(4))  # 1
print(ff(5))  # 1

Example: decorator as a class

# example of decorator on class


class Dog:

    def __init__(self, hh):
        print("Dog.__init__() called")
        hh()  # Prove that function definition has completed

    def __call__(self):
        print("Dog.__call__() called")

@Dog  # this forces Dog to be called when ff is called
def ff():
    print("ff() called")

ff()
# Dog.__init__() called
# ff() called
# Dog.__call__() called

Example 2

class Gate(object):

    def __init__(self, f):
        self.f = f

    def __call__(self):
        print("enter", self.f.__name__)
        self.f()
        print("exit", self.f.__name__)

@Gate
def f1():
    print("f1() called")

@Gate
def f2():
    print("f2() called")

f1()
f2()

# enter f1
# f1() called
# exit f1
# enter f2
# f2() called
# exit f2

Explain python decorator

explain python decorator

ai answer

Python decorators are a concise way to wrap (modify or enhance) a function or method without changing its source code. They are syntactic sugar for higher-order functions: functions that take another function as input and return a new function.

Core Idea

Functions in Python are first-class objects — they can be passed around, assigned to variables, and returned from other functions. A decorator takes a function, adds behavior around it, and returns the modified version.

Basic Example (Without the @ Syntax)

def my_decorator(func):
    def wrapper():
        print("Something before the function runs")
        func()
        print("Something after the function runs")
    return wrapper

def say_hello():
    print("Hello!")

# Manually applying the decorator
say_hello = my_decorator(say_hello)
say_hello()

Output:

Something before the function runs
Hello!
Something after the function runs

Using the @ Syntax

The @ symbol is just convenient syntax for the assignment above:

@my_decorator
def say_hello():
    print("Hello!")

say_hello()  # Same result as above

This is equivalent to say_hello = my_decorator(say_hello).

Decorators That Accept Arguments

Most real-world functions take arguments, so the wrapper must accept *args and **kwargs:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before")
        result = func(*args, **kwargs)
        print("After")
        return result
    return wrapper

@my_decorator
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

Preserving Function Metadata (functools.wraps)

Without help, the decorated function loses its original name, docstring, etc. Always use functools.wraps:

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Before")
        return func(*args, **kwargs)
    return wrapper

Decorator Factories (Decorators with Their Own Arguments)

When the decorator itself needs parameters, you write a function that returns a decorator:

def repeat(times):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hi():
    print("Hi!")

say_hi()  # Prints "Hi!" three times

Common Built-in Decorators

Multiple Decorators

They apply bottom-up (closest to the function first):

@decorator_a
@decorator_b
def f():
    pass
# Equivalent to: f = decorator_a(decorator_b(f))

Class-Based Decorators

You can also write a class with a __call__ method:

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Call {self.count}")
        return self.func(*args, **kwargs)

@CountCalls
def hello():
    print("Hello")

Key Takeaways

This pattern is one of the most powerful and widely used features in modern Python.

Reference

Python, Function and Class