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
, the program actually executes 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!