Decorators
In Python, a decorator is a design pattern that allows you to modify the behavior of a function or a class without changing its source code. It helps to enhance or modify the functionality of an existing object dynamically at runtime.
In simple terms, a decorator wraps another function or class and allows you to add extra functionality before or after the wrapped object executes. This allows you to extend the behavior of functions or classes without modifying their original code.
Create a Decorator
To create a decorator, you need to follow these steps:
- Define a function that will act as the decorator. This function will take in the function or class to be decorated as an argument.
- Inside the decorator function, define a new function or class that will add the additional functionality.
- Optionally, you can modify or enhance the original function or class by wrapping it with the new function or class.
- Return the new function or class defined in the decorator function.
Decorator Examples
Here's an example of a basic decorator that logs the execution time of a function:
import timedef execution_time_decorator(func):def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()execution_time = end_time - start_timeprint(f"Function {func.__name__} took {execution_time} seconds to execute.")return resultreturn wrapper@execution_time_decoratordef some_function():# Function's code herepasssome_function()
In this example, the `executiontimedecorator` function is defined as a decorator. It takes a function as an argument, wraps it with the `wrapper` function that logs the execution time, and returns the wrapped function.
By using the `@executiontimedecorator` syntax above the `somefunction` definition, we apply the decorator to that function. Now, whenever `somefunction` is called, it will automatically log the execution time.
This is just a basic example, and decorators can be used for various purposes, such as logging, authentication, memoization, and more. Decorators enhance code reusability and are an essential part of Python's dynamic programming features.
Basic Decorator
import functoolsimport timedef how_to_build_a_decorator(func):@functools.wraps(func)def wrapper_decorator(*args, **kwargs):# Do something before running the function.value = func(*args, **kwargs)# Do something after running the functionreturn valuereturn wrapper_decorator
Debug Decorator
def debug(func):"""Print the function signature and return value."""@functools.wraps(func)def wrapper_debug(*args, **kwargs):# Do something before running the function.args_repr = [repr(a) for a in args] # 1kwargs_repr = [f"{k}= {v!r}" for k, v in kwargs.items()] # 2signature = ", ".join(args_repr + kwargs_repr) # 3print(f"Calling {func.__name__}({signature})")value = func(*args, **kwargs)print(f"{func.__name__!r} returned {value!r}") # 4# Do something after running the functionreturn valuereturn wrapper_debugPLUGINS = dict()
Slow Down Decorator
def slow_down(func):"""Sleep 1 second before calling the function."""@functools.wraps(func)def wrapper_slow_down(*args, **kwargs):time.sleep(1)return func(*args, **kwargs)return wrapper_slow_down
Timer Decorator
def timer(func):"""Print the runtime for decorated function."""@functools.wraps(func)def wrapper_timer(*args, **kwargs):start_time = time.perf_counter() # Do something before running the function.value = func(*args, **kwargs)end_time = time.perf_counter() # Do something after running the functionrun_time = end_time - start_timeprint(f"Finished {func.__name__!r} in {run_time:.4f} seconds.")return valuereturn wrapper_timer
Multiple decorators
def make_bold(func):def wrapper_make_bold():return f"<b>{func()}</b>"return wrapper_make_bolddef make_italic(func):def wrapper_make_italic():return f"<i>{func()}</i>"return wrapper_make_italicif __name__ == "__main__":@make_bold@make_italicdef say():return "something"print(say())