Decorator in Python

A decorator is one of the python’s useful syntaxes that literally decorate functions, methods and even classes.

A Typical Example of Python Decorator

Many blog posts demonstrate decorators using logging or print statements, as shown in the following example:

import time

# make decorator 
def exec_time(func):
    def wrapper(*args, **kwargs):
        starttime = time.perf_counter()
        res = func(*args, **kwargs)
        func_exec_time = time.perf_counter() - starttime
        print(f"function - `{func.__name__}` conducted in {func_exec_time}s")
        return res
    return wrapper

# decorate function with `@exec_time`
@exec_time
def add(a:int, b:int):
    return a + b

# usage
add(100, 30) 

The expected output will be as follows:

function - `add` conducted in 6.659999999998611e-07s

A Practical Example of Python Decorator

However, my favorite use case for Python decorators is implementing the Singleton pattern with existing functions, methods, or classes as follows:

import threading

# make singleton decorator
def singleton(func):
    lock = threading.Lock()  # If you need thread-safe one

    def wrapper(*args, **kwargs):
        with lock:
            if wrapper.instance is None:
                wrapper.instance = func(*args, **kwargs)
        return wrapper.instance

    wrapper.instance = None  # Ensuring attribute always exists before execution
    return wrapper

# decorate class with `@singleton`
@singleton
class MySingleton:
    def __init__(self, value):
        self.value = value

    def get_value(self):
        return self.value

# usage
instance1 = MySingleton(100)
instance2 = MySingleton(30)

print("is instance 1 is the same with instance 2?")
print(instance1 is instance2)
print("is instance 1 value")
print(instance1.get_value())
print("is instance 2 value")
print(instance2.get_value())

Then the expected output will be as follows:

is instance 1 is the same with instance 2?
True
is instance 1 value
100
is instance 2 value
100

Conclusion

Python decorator is powerful because it helps to modify the functionilty of codes without less modifications.