Python Decorators 101
A lot has been written over time about these cheeky little creatures and I couldn't possibly shy away from throwing a bit of code together.
Before I start, what is a decorator? It's a little monkey that adds extra functionality to your function (or class).
Let's hit it. Create a file called decorator.py
and copy and paste (or type) the below example:
In this case it's hopefully obvious that this decorator (1) takes a function (callable) as an input (2) prints some metadata about the function passed and (3) outputs the very same function. Let's go ahead and decorate.
So this is how you decorate! That looks familiar right? Like @staticmethod
or @property
in a class or @login_required
in django. Let's get into out REPL and execute get_version()
python -i decorator.py
>>> get_version()
{'function_name': 'get_version', 'docstring': 'Returns your python interpreter version'}
'3.9.10 (main, Jan 29 2022, 05:27:31) \n[GCC 10.2.1 20210110]'
We can see the metadata at runtime but if we do the below:
>>> get_version.__name__
'call'
>>> get_version.__doc__
>>>
What happened? __name__
should be get_version
and the docstring should definitely not be empty.
functools
to the rescue
Let's import wraps
from functools
and add it to our metadata
decorator like this:
import typing
from functools import wraps
def metadata(func: typing.Callable) -> typing.Callable:
@wraps(wrapped=func)
def call(*args, **kwargs):
metadata_ = {
"function_name": func.__name__,
"docstring": func.__doc__
}
print(metadata_)
return func(*args, **kwargs)
return call
Repeating the previous exercise we now see the metadata remains intact.
# python -i decorator.py
>>> get_version.__name__
'get_version'
>>> get_version.__doc__
' Returns your python interpreter version '
>>>
Here's a slightly more complex decorator example if you feel adventurous
Have a read about functools.wraps too if you wish to know more.
Enjoy coding!