How to use decorators in Python
You may have already found a decorator in some Python code. For example, the following is commonly used to create static methods within a class:
class Person:
@staticmethod
def some_static_method():
pass
But what does really do a decorator and how to use it in order to improve your coding experience in Python?
The concept ☑️
A decorator is a function that takes another function as an argument, executes it within an internal function and returns the internal one.
This concept is clarified in the example below 😅:
def decorator(func):
def do_something(*args, **kwargs):
# do what you need to do here
print("I'm doing something here...")
# execute the function, returning its result
return func(*args, **kwargs)
return do_something
@decorator
def my_function(*args):
print("Hi there! I'm a function :)")
What is happening there?
Every time you call my_function
, you're actually executing do_something
, the inner function of the decorator! It allows you to perform some action before running the decorated functions themselves.
Ok, but... Please, give me some better examples 🤷♂️
So, here we go!
Counting time
Let's suppose that you want to know how long a function takes to run. A simple way to do it is by creating a decorator that acts as a stopwatch:
import time
# decorator
def stopwatch(func):
# inner function
def run(*args, **kwargs):
# start counting
start = time.time()
# run function
response = func(*args, **kwargs)
# stop counting
end = time.time()
# print the timing
print(f"Timing for running {func.__name__}: {end - start}")
# return the executed function value
return response
# it runs every time you call a function decorated by @stopwatch
return run
Let's test it with some function:
@stopwatch
def test():
print('Test me!')
test()
The output will be something like this:
Test me!
Timing for running test: 5.364418029785156e-05
Storing values
In this example, we'll be greeting some people, but we don't wanna greet someone more than a single time. Thus we'll use a decorator for storing the names that were already passed as arguments for our greeting function, avoiding that kind of problem.
def just_one_time(func):
# list of greeted people
names_list = []
# inner function
def check_name(*args, **kwargs):
name = args[0]
# Program stops if name already exists in list
if name in names_list:
raise Exception(f"{name} was already greeted!")
# Else, list is updated and the decorated function runs
else:
names_list.append(name)
return func(*args, **kwargs)
return check_name
@just_one_time
def greet(name):
print(f"Welcome {name}!")
greet('George')
greet('Samantha')
greet('Samantha') # Exception here
The above code will be executed until we try to greet Samantha twice. 😉
Decorators with custom arguments 🤲🏻
It would be nice to pass arguments to the decorators because that way we could make them respond according to the needs of each function.
Wait... We can do it! 😃
All we need to do is wrapping our old decorator inside a new function. See how it works:
# new decorator
def decorator(*args, **kwargs):
# our old decorator
def inner_wrapper(func):
# main function
def inner(*args, **kwargs):
# do something
return func(*args, **kwargs)
return inner
# get the function and run inner_wrapper
if len(args) == 1 and callable(args[0]):
func = args[0]
return inner_wrapper(func)
# use custom arguments
print(args, kwargs)
return inner_wrapper
Now, we can use our new decorator this way:
@decorator('simple argument', key_argument='some value')
def do_something():
...
Keep it in mind 🧠
In the example above, our decorator runs along with the rest of the code (a single time), while its inner function runs every time we call do_something()
.
Bonus
Another cool fact about decorators is that you can use more than one for each function:
@first
@second
def my_function():
...
Conclusion
- Feel free to use decorators when you intend to prepare the way for a function every time it's called.
- Decorators offer a good way to control what happens when you call a method.
- Nothing prevents you from using them in class methods, as we see at the start of this article. 😉
I hope this brief explanation has been helpful to you!
No Comments Yet