









Python Decorators
Decorators
Decorators in Python allows you to modify the behavior of functions or classes. If you're coming from a beginner’s perspective, think of a decorator as a "wrapper" that adds extra features to an existing function without changing its core code.
Why Use Decorators?
Imagine you want to log the execution time of various functions or check user authentication before executing a task. You could repeat that logic inside every function... or, you could just use a decorator. It keeps your code clean, modular, and reusable.
Functions are First-Class Citizens
Before diving into decorators, it’s important to know that in Python, functions are first-class objects. This means:
- You can pass a function as an argument.
- You can return a function from another function.
- You can assign a function to a variable.
def greet():
return "Hello!"
say_hello = greet
print(say_hello())
Hello!
Basic Decorator Structure
A decorator is simply a function that takes another function as input and returns a new function with added behavior.
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
def say_hi():
print("Hi!")
decorated = my_decorator(say_hi)
decorated()
Something is happening before the function is called.
Hi!
Something is happening after the function is called.
Using the @
Syntax (Syntactic Sugar)
Python provides a cleaner way to apply decorators using the @
symbol.
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hi():
print("Hi!")
say_hi()
Something is happening before the function is called.
Hi!
Something is happening after the function is called.
Decorators with Arguments
If your target function has parameters, your wrapper needs to accept them too.
def log_args(func):
def wrapper(*args, **kwargs):
print(f"Arguments passed: {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@log_args
def add(x, y):
return x + y
print(add(5, 7))
Arguments passed: (5, 7), {}
12
Returning Values from Decorated Functions
Always remember to return the result of the original function if it produces one.
Using functools.wraps
When you use decorators, the metadata of the original function (like its name or docstring) can get lost. To preserve it, use functools.wraps
.
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper
@my_decorator
def greet(name):
"Greets a user by name"
print(f"Hello, {name}")
print(greet.__name__)
print(greet.__doc__)
Before function call
Hello, Alice
After function call
greet
Greets a user by name
Common Use Cases of Decorators
- Logging
- Access control (authentication/authorization)
- Timing functions
- Memoization (caching)
- Validation
Built-in Decorators in Python
Python includes several built-in decorators:
@staticmethod
@classmethod
@property
class MyClass:
@staticmethod
def static_method():
print("This is a static method.")
@classmethod
def class_method(cls):
print(f"This is a class method from {cls.__name__}")
@property
def name(self):
return "TutorialKart"
obj = MyClass()
obj.static_method()
obj.class_method()
print(obj.name)
This is a static method.
This is a class method from MyClass
TutorialKart
Best Practices & Things to Watch For
- Always use
functools.wraps
when writing decorators. - Make sure your decorator does not block or break return values.
- Understand that decorators are executed at the time of function definition, not at runtime.
- Use decorators to separate logic concerns (e.g., don’t mix validation and logging inside the core function).
Verification Steps
To ensure your decorator works as expected:
- Call the decorated function and check if the new behavior is added.
- Check that arguments and return values are intact.
- Verify metadata using
__name__
and__doc__
. - Use logging statements in the decorator to debug step-by-step.
Conclusion
Decorators are one of Python’s elegant ways to inject functionality without altering existing code.