Decorators
What are Decorators?
Imagine you have a sandwich. Sometimes, you want to add extra cheese or sauce to make it tastier. Decorators in Python are like those extra toppings you add to your sandwich. They are special tools that let you add extra features to your functions without changing the function itself.
Why Use Decorators?
Decorators help us:
- Add extra functionality to our functions.
- Keep our code clean and organized.
- Reuse code by applying the same decorator to multiple functions.
How Do Decorators Work?
Let’s start with a simple example. Imagine we have a function that says hello:
def say_hello():
print("Hello!")
Now, what if we want to add some excitement to our greeting? We can use a decorator for that.
Creating a Simple Decorator
A decorator is a function that takes another function as an argument, adds some extra features, and returns a new function. Here’s a simple decorator that adds excitement:
def add_excitement(func):
def wrapper():
print("Excited to say:")
func()
print("Yay!")
return wrapper
Let’s break it down:
add_excitement(func)
: This is our decorator. It takes a functionfunc
as an argument.def wrapper()
: Inside the decorator, we define a new function calledwrapper
.print("Excited to say:")
: Thewrapper
function adds some excitement before calling the original function.func()
: Thewrapper
function calls the original function.print("Yay!")
: Thewrapper
function adds some more excitement after calling the original function.return wrapper
: The decorator returns thewrapper
function.
Using the Decorator
Now, let’s use our decorator to add excitement to the say_hello
function:
@add_excitement
def say_hello():
print("Hello!")
The @add_excitement
line is how we apply the decorator to the say_hello
function. When we call say_hello()
, it will actually call the wrapper
function inside the decorator.
say_hello()
Output:
Excited to say:
Hello!
Yay!
Practice Question 1
Create a decorator called shout
that makes a function print its message in uppercase letters.
Solution:
def shout(func):
def wrapper():
result = func()
print(result.upper())
return wrapper
@shout
def greet():
return "hello there"
greet()
Output:
HELLO THERE
Decorators with Arguments
Decorators can also take arguments. For example, let’s create a decorator that repeats the message a certain number of times.
def repeat(times):
def decorator(func):
def wrapper():
for _ in range(times):
func()
return wrapper
return decorator
Here’s how to use it:
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
Output:
Hello!
Hello!
Hello!
Practice Question 2
Create a decorator called debug
that prints the name of the function being called and its arguments.
Solution:
def debug(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with arguments {args} and {kwargs}")
return func(*args, **kwargs)
return wrapper
@debug
def say_hello(name):
print(f"Hello, {name}!")
say_hello("Alice")
Output:
Calling say_hello with arguments ('Alice',) and {}
Hello, Alice!
Summary
- Decorators are like magic wrappers that add extra features to functions.
- They help keep code clean and reusable.
- Decorators can take arguments and handle multiple function arguments.
Practice Makes Perfect
Try creating your own decorators for fun tasks, like logging, timing, or even modifying return values. The more you practice, the more magical your code will become!
Happy coding! 🎉