Python decorators are a powerful and expressive tool that allows programmers to modify the behavior of a function or class method without modifying its actual source code. Decorators are heavily used in frameworks such as Flask and Django, and also in standard libraries such as unittest and functools.
A decorator in Python is a function that takes another function as input and extends or alters its behavior. Decorators make code more modular, readable, and DRY (Donβt Repeat Yourself).
def greet():
return "Hello!"
print(greet())
Now suppose we want to add some logging functionality whenever `greet` is called. Instead of modifying the function directly, we can use a decorator.
def log_decorator(func):
def wrapper():
print("Function is about to run")
result = func()
print("Function has run")
return result
return wrapper
@log_decorator
def greet():
return "Hello!"
print(greet())
The output will be:
Function is about to run
Function has run
Hello!
The `@decorator_name` syntax is just syntactic sugar for:
greet = log_decorator(greet)
Sometimes, the function you want to decorate takes arguments. You need to make sure the wrapper can accept arbitrary arguments.
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f"Calling function {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_decorator
def add(x, y):
return x + y
print(add(10, 5))
Itβs important that the wrapper returns the result of the original function to maintain expected functionality.
def log_decorator(func):
def wrapper(*args, **kwargs):
print("Logging before function call")
result = func(*args, **kwargs)
print("Logging after function call")
return result
return wrapper
You can stack multiple decorators on a single function. The decorators are applied from the inside out (bottom to top).
def decorator_one(func):
def wrapper(*args, **kwargs):
print("Decorator One")
return func(*args, **kwargs)
return wrapper
def decorator_two(func):
def wrapper(*args, **kwargs):
print("Decorator Two")
return func(*args, **kwargs)
return wrapper
@decorator_one
@decorator_two
def say_hello():
print("Hello!")
say_hello()
If you want your decorator itself to accept arguments, you need to add another layer of function nesting.
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("Hi!")
greet()
When wrapping functions with decorators, Python loses metadata like function name and docstring. Use `functools.wraps` to preserve it.
from functools import wraps
def log_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Calling function:", func.__name__)
return func(*args, **kwargs)
return wrapper
@log_decorator
def greet():
"""Greet function docstring"""
print("Hello!")
print(greet.__name__)
print(greet.__doc__)
You can also use decorators on classes. Here's a simple example:
def decorator(cls):
cls.is_decorated = True
return cls
@decorator
class MyClass:
pass
print(MyClass.is_decorated)
import time
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"Execution time: {end - start:.4f} seconds")
return result
return wrapper
@timer
def slow_function():
time.sleep(2)
return "Done"
slow_function()
def require_admin(func):
def wrapper(user_role, *args, **kwargs):
if user_role != "admin":
return "Access Denied"
return func(user_role, *args, **kwargs)
return wrapper
@require_admin
def view_dashboard(user_role):
return "Dashboard content"
print(view_dashboard("admin"))
print(view_dashboard("user"))
class MyClass:
@staticmethod
def static_method():
print("I am a static method")
@classmethod
def class_method(cls):
print("I am a class method")
MyClass.static_method()
MyClass.class_method()
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
temp = Temperature(25)
print(temp.fahrenheit)
Sometimes, you want to apply multiple parameterized decorators. The syntax is the same:
def outer_decorator(param):
def actual_decorator(func):
def wrapper(*args, **kwargs):
print(f"Decorator parameter: {param}")
return func(*args, **kwargs)
return wrapper
return actual_decorator
@outer_decorator("Value1")
@outer_decorator("Value2")
def my_func():
print("Executing function")
my_func()
def bad_decorator(func):
print("Decorating")
# Forgot to return a function!
@bad_decorator
def test():
print("Test function")
This will raise an error because the decorator doesn't return a callable.
def bad_decorator(func):
def wrapper():
print("Something")
return wrapper
@bad_decorator
def add(a, b):
return a + b
print(add(1, 2)) # This will print None
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Welcome to the homepage"
@app.route('/about')
def about():
return "About Page"
from functools import lru_cache
@lru_cache(maxsize=32)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
print(fib(30))
If you're using multiple decorators, especially parameterized ones, debugging can be tricky. Always use `functools.wraps` and avoid deeply nested logic unless necessary.
Python decorators provide a concise, readable, and reusable way to alter or enhance functions and methods. They are foundational to Python's design, used extensively in web development, data science, and system utilities. Once you master decorators, youβll find yourself writing more elegant, modular, and DRY code. From logging, authentication, caching, to even more complex metaprogramming, decorators are a Swiss Army knife in the Python developer's toolkit.
Python is commonly used for developing websites and software, task automation, data analysis, and data visualisation. Since it's relatively easy to learn, Python has been adopted by many non-programmers, such as accountants and scientists, for a variety of everyday tasks, like organising finances.
Learning Curve: Python is generally considered easier to learn for beginners due to its simplicity, while Java is more complex but provides a deeper understanding of how programming works.
The point is that Java is more complicated to learn than Python. It doesn't matter the order. You will have to do some things in Java that you don't in Python. The general programming skills you learn from using either language will transfer to another.
Read on for tips on how to maximize your learning. In general, it takes around two to six months to learn the fundamentals of Python. But you can learn enough to write your first short program in a matter of minutes. Developing mastery of Python's vast array of libraries can take months or years.
6 Top Tips for Learning Python
The following is a step-by-step guide for beginners interested in learning Python using Windows.
Best YouTube Channels to Learn Python
Write your first Python programStart by writing a simple Python program, such as a classic "Hello, World!" script. This process will help you understand the syntax and structure of Python code.
The average salary for Python Developer is βΉ5,55,000 per year in the India. The average additional cash compensation for a Python Developer is within a range from βΉ3,000 - βΉ1,20,000.
Copyrights © 2024 letsupdateskills All rights reserved