- Aliases
- and operator
- Booleans
- Classes
- Code blocks
- Comments
- Conditional statements
- Console
- Data structures
- datetime module
- Decorator
- Dictionaries
- Docstrings
- enum
- enumerate() function
- Equality operator
- Exception handling
- False
- File handling
- Floats
- For loops
- Formatted strings
- Functions
- Generator
- Greater than operator
- Greater than or equal to operator
- If statement
- in operator
- Indices
- Inequality operator
- Integers
- Iterator
- Lambda function
- Less than operator
- Less than or equal to operator
- List append() method
- List comprehension
- List insert() method
- List pop() method
- List sort() method
- Lists
- Logging
- map() function
- Match statement
- Math module
- Modules
- Multiprocessing
- Multithreading
- None
- not operator
- OOP
- or operator
- Parameters
- print() function
- Random module
- range() function
- Recursion
- Regular expressions
- requests Library
- return statement
- round() function
- Sets
- SQLite
- String join() method
- String replace() method
- String split() method
- Strings
- time.sleep() function
- True
- try...except statement
- Tuples
- Variables
- While loops
- Zip function
PYTHON
Python Decorator: Syntax, Usage, and Examples
A Python decorator lets you modify or extend the behavior of a function or class without changing its actual code. You wrap the function inside another function to add extra functionality like logging, authentication, or performance tracking.
How to Use a Decorator in Python
A decorator in Python follows a simple structure:
def my_decorator(func):
def wrapper():
print("Something before the function runs.")
func()
print("Something after the function runs.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
How Decorators Work
- The
my_decorator
function takes another function (func
) as an argument. - Inside it,
wrapper()
runs extra code before and after callingfunc()
. - When you use
@my_decorator
beforesay_hello()
, it automatically passessay_hello()
intomy_decorator()
and replaces it withwrapper()
.
Using Decorators with Function Arguments
Functions often take arguments, and decorators need to handle them too. You can use *args
and **kwargs
to make sure the decorator works with any function.
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"Calling function {func.__name__} with arguments {args} {kwargs}")
return func(*args, **kwargs)
return wrapper
@log_function_call
def add(a, b):
return a + b
print(add(3, 5)) # Output: Calling function add with arguments (3, 5) {} \n 8
When to Use Python Decorators
Decorators are useful when you need to:
- Modify function behavior without changing its code
- Example: Automatically logging function calls.
- Reuse code across multiple functions
- Example: Enforcing authentication in web applications.
- Improve readability and maintainability
- Example: Making functions cleaner by handling repetitive logic separately.
Examples of Python Decorators
Measuring Execution Time
A timer decorator lets you see how long a function takes to run.
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} executed in {end - start:.5f} seconds")
return result
return wrapper
@timer
def slow_function():
time.sleep(2)
slow_function() # Output: slow_function executed in 2.000xx seconds
Passing Arguments to a Decorator
Sometimes, you want to customize how a decorator works. Instead of modifying the decorator function directly, you can use an extra function level.
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet():
print("Hello!")
greet() # Output: "Hello!" printed 3 times
Using a Class as a Decorator
You can also create decorators using classes. This approach gives you more flexibility, especially when you need to store state between function calls.
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} has been called {self.count} times")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
Learn More About Python Decorators
The @property
Decorator
The @property
decorator lets you define getter methods without explicitly calling them. You use it to control how a class’s attributes are accessed.
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
p = Person("Alice")
print(p.name) # Output: Alice
The @lru_cache
Decorator (Memoization)
Python’s functools.lru_cache
decorator caches results so that repeated function calls with the same arguments don’t recompute the results.
from functools import lru_cache
@lru_cache(maxsize=3)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
print(fib(10)) # Output: 55
Using a Lambda Function in a Decorator
You can simplify some decorators with lambda functions. This is useful for short, single-expression decorators.
def double_return(func):
return lambda *args, **kwargs: func(*args, **kwargs) * 2
@double_return
def square(x):
return x * x
print(square(3)) # Output: 18 (3*3, doubled)
A Custom Cache Decorator
Instead of using @lru_cache
, you can build your own caching decorator.
def cache_decorator(func):
cache = {}
def wrapper(*args):
if args in cache:
print("Fetching from cache")
return cache[args]
result = func(*args)
cache[args] = result
print("Storing in cache")
return result
return wrapper
@cache_decorator
def add(a, b):
return a + b
print(add(2, 3)) # Output: Storing in cache \n 5
print(add(2, 3)) # Output: Fetching from cache \n 5
Using Decorators for Authentication
In web applications, decorators help enforce authentication.
def requires_login(func):
def wrapper(user):
if not user.get("logged_in"):
print("Access Denied: Please log in")
return
return func(user)
return wrapper
@requires_login
def view_profile(user):
print(f"Welcome, {user['name']}!")
user1 = {"name": "Alice", "logged_in": True}
user2 = {"name": "Bob", "logged_in": False}
view_profile(user1) # Output: Welcome, Alice!
view_profile(user2) # Output: Access Denied: Please log in
Chaining Multiple Decorators
You can stack multiple decorators on a single function. Python applies them from bottom to top.
def uppercase(func):
def wrapper():
return func().upper()
return wrapper
def exclaim(func):
def wrapper():
return func() + "!"
return wrapper
@uppercase
@exclaim
def greet():
return "hello"
print(greet()) # Output: HELLO!
Best Practices When Using Decorators
-
Use
functools.wraps()
When defining a decorator, use
@functools.wraps(func)
to preserve the original function’s name and docstring.from functools import wraps def my_decorator(func): @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper
-
Keep decorators simple
If a decorator becomes too complex, consider refactoring it into multiple functions or a class.
-
Document your decorators
If you write custom decorators for a project, add comments or docstrings to explain what they do.
Python decorators let you modify functions dynamically, making them powerful tools for writing cleaner, reusable code. You can use them for logging function calls, enforcing security, or improving performance, decorators help you keep your code DRY (Don't Repeat Yourself) while adding flexibility.
Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.