Introduction to Decorators
Decorators are a powerful and expressive feature in Python that allow you to modify or extend the behavior of functions or classes without permanently modifying them.
# Basic decorator example
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_hello():
print("Hello!")
# Calling the decorated function
say_hello()
# Output:
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.
Decorators are essentially functions that take another function as an argument and return a modified function.
Decorator Syntax
Python provides a special syntax for applying decorators using the @ symbol. This is called "pie syntax".
# The @decorator syntax is equivalent to:
def original_function():
pass
# These two are equivalent:
decorated_function = my_decorator(original_function)
@my_decorator
def original_function():
pass
# Decorators can be stacked:
@decorator1
@decorator2
def my_function():
pass
# Equivalent to: my_function = decorator1(decorator2(my_function))
The decorator syntax makes your code cleaner and more readable by keeping the decoration close to the function definition.
Decorators with Arguments
Decorators can accept arguments by adding another layer of nesting to the decorator function.
# Decorator with arguments
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello {name}")
greet("Alice")
# Output:
# Hello Alice
# Hello Alice
# Hello Alice
This pattern allows decorators to be customized when they're applied, making them more flexible and reusable.
Class Decorators
Decorators can also be applied to classes, modifying or extending their behavior.
# Class decorator example
def add_methods(cls):
def new_method(self):
return "This is a new method"
cls.new_method = new_method
return cls
@add_methods
class MyClass:
def __init__(self, value):
self.value = value
obj = MyClass(42)
print(obj.new_method()) # "This is a new method"
# Built-in class decorators
@dataclass
class Point:
x: int
y: int
p = Point(1, 2)
print(p) # Point(x=1, y=2)
Class decorators are commonly used for adding methods, modifying attributes, or implementing design patterns.
Built-in Decorators
Python comes with several useful built-in decorators for common tasks.
# @staticmethod - doesn't receive implicit first argument
class Math:
@staticmethod
def add(x, y):
return x + y
# @classmethod - receives class as first argument
class Person:
def __init__(self, name):
self.name = name
@classmethod
def from_dict(cls, data):
return cls(data['name'])
# @property - defines a getter method
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value >= 0:
self._radius = value
else:
raise ValueError("Radius must be positive")
# @functools.lru_cache - memoization decorator
from functools import lru_cache
@lru_cache(maxsize=32)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
These built-in decorators provide functionality for method types, property management, and performance optimization.
Real-world Decorator Examples
Decorators are commonly used in web frameworks, testing, and other practical applications.
# Timing decorator
import time
from functools import wraps
def timing(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"Function {func.__name__} took {end-start:.4f} seconds")
return result
return wrapper
@timing
def slow_function():
time.sleep(1)
slow_function() # Prints timing information
# Flask route decorator example
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Hello, World!"
# Login required decorator
def login_required(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not current_user.is_authenticated:
return redirect(url_for('login'))
return func(*args, **kwargs)
return wrapper
Decorators are widely used in web frameworks (Flask, Django), testing (pytest fixtures), and many other Python libraries.
Python Decorators Videos
Master Python decorators with these handpicked YouTube tutorials:
Learn the fundamentals of Python decorators:
Deep dive into decorator techniques:
Python's standard decorators:
Real-world decorator use cases: