Python Decorators

A decorator is an advanced feature in Python used todynamically extend the functionality of functions or classes without modifying the original function code。

Essentially, a decorator is a function: it receives a function as an argument and returns a new function (usually an enhanced version of the original function).

Decorators are applied using the@decorator_namesyntax placed before a function or method definition.

Python also provides some built-in decorators, such as@staticmethodand@classmethod。

Common use cases:

  • Logging:Record function call information, parameters, and return values
  • Performance statistics:Measure function execution time
  • Access control:Restrict function access permissions
  • Caching:Cache function results to improve performance

Basic Syntax

The core idea of a decorator is:using one function to "wrap" another function。

Syntax

def decorator_function(original_function):
    def wrapper(*args, **kwargs):
        # Before calling
        print("Before execution")

        result = original_function(*args, **kwargs)

        # After calling
        print("After execution")

        return result
    return wrapper

@decorator_function
def target_function():
    print("Original function execution")

Explanation:

  • decorator_function: decorator function (receives the original function)
  • wrapper: wrapper function (actually executed)
  • @decorator_function: equivalent to function replacement

Equivalent form:

target_function = decorator_function(target_function)

👉 When callingtarget_function(), what is actually executed iswrapper()


Using Decorators

Decorators are applied using the@syntactic sugar placed before the function definition:

@time_logger
def target_function():
    pass

Equivalent to:

def target_function():
    pass

target_function = time_logger(target_function)

This mechanism allows us to uniformly add functionality (such as logging, permissions, etc.) without modifying the original function.


Example: Printing Logs

def my_decorator(func):
    def wrapper():
        print("Before function execution")
        func()
        print("After function execution")
    return wrapper

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

say_hello()

Output:

函数执行前
Hello!
函数执行后
  • my_decoratorReceivessay_hello
  • @my_decoratorReplaces the original function

Decorators with Parameters

If the original function has parameters, you need to, in thewrapperuse*args, **kwargs:

Example

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

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

greet("Alice")

Output:

执行前
Hello, Alice!
执行后

Explanation:Using*args, **kwargscan accommodate functions with any number of parameters.


Decorators with Parameters (Advanced)

def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()

Explanation:This is a "decorator factory"; the outer function is used to receive parameters.

Hello!
Hello!
Hello!


Class Decorators

In addition to functions, decorators can also be applied to classes.

A class decorator receives a class and returns a modified class or a wrapper class.

  • Enhance class methods
  • Control the instantiation process
  • Implement functionality such as singletons and logging

Function-based Class Decorators

Example

def log_class(cls):
    class Wrapper:
        def __init__(self, *args, **kwargs):
            self.wrapped = cls(*args, **kwargs)

        def __getattr__(self, name):
            return getattr(self.wrapped, name)

        def display(self):
            print("Before call")
            self.wrapped.display()
            print("After call")

    return Wrapper

@log_class
class MyClass:
    def display(self):
        print("Original method")

obj = MyClass()
obj.display()
Before call
Original method
After call

Class-based Class Decorators

Example: Singleton Pattern

class SingletonDecorator:
    def __init__(self, cls):
        self.cls = cls
        self.instance = None

    def __call__(self, *args, **kwargs):
        if self.instance is None:
            self.instance = self.cls(*args, **kwargs)
        return self.instance

@SingletonDecorator
class Database:
    def __init__(self):
        print("Initialization")

db1 = Database()
db2 = Database()
print(db1 is db2)
初始化
True

Built-in Decorators

Commonly used built-in decorators:

  1. @staticmethod: defines a static method
  2. @classmethod: defines a class method
  3. @property: turns a method into a property

Example

class MyClass:
    @staticmethod
    def static_method():
        print("Static method")

    @classmethod
    def class_method(cls):
        print(cls.__name__)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

Stacking Multiple Decorators

Multiple decoratorswrap the function from bottom to top during the definition phase,and execute from top to bottom during the calling phase:

Example

def decorator1(func):
    def wrapper():
        print("Decorator 1")
        func()
    return wrapper

def decorator2(func):
    def wrapper():
        print("Decorator 2")
        func()
    return wrapper

@decorator1
@decorator2
def say_hello():
    print("Hello!")

say_hello()

Final output:

Decorator 1
Decorator 2
Hello!

Core Summary

Decorator = a function that wraps a function + extending functionality without modifying the original code
  • The @ syntax is essentially function replacement
  • wrapper is the function that is actually executed
  • It is recommended to use *args, **kwargs for greater versatility
  • Supports functions, classes, and even decorators with parameters
Other Extensions