Decorators allow you to attach new functionality to a method without modifying it's behavior. This can be easily implemented in python because functions are treated as first class citizens (i.e a can be passed as arguments or returned from functions).
To put in simple terms:
A Decorator is a function the takes a function and returns a function.
They are used extensively in some popular frameworks like Flask.
This is a simple decorator that prints a statement before and after a function is called.
def dec(f: Callable) -> Callable:
Line 1: We define a new function (which will soon be our decorator) called dec which takes a function f as a parameter. I have added few type hints using
import typingbut it's not necessary and not enforced by the interpreter.
Line 2: We define a new inner function g which takes all the ordinary arguments and keyword arguments that function f takes.
Line 3: Just an example of something that happens before executing function f.
Line 4: Call function f with all of it's arguments and store result in variable r.
Line 5: Just and example of something that happens after executing function f.
Line 6: Returns the result of function f in order to keep it working as intended.
Line 7: Returns the newly decorated function g.
Now let's decorate a simple
add function with the new decorator:
The difference in function name and memory address (line 2 & line 5) will be discussed below.
Here is an applicable example of a decorator that enables you to easily determine how long does a function takes in order to finish execution.
def timer(f: Callable) -> Callable:
You replace the
@timer or even stack multiple decorators on top of each other:
- Line 1: Output of decorator
- Line 2: Output of decorator
- Line 3, 4: The rest of
- Line 5: Output of function
Let's assume you want to keep track of the output of different functions in some sort of a global set. This can be useful for some sort of a simple unit testing solution.
TEST = set()
Not much different than the previous decorators, only is adding a tuple that contains the function name, arguments and result to a global set called
If anything went wrong with the assert statement; the interpreter will halt execution and raise and assert exception.
In order to add additional functionality to a method, the decorator generate a new function with a new name in a new memory address which might confuse some tools and debuggers.
To avoid renaming your function and keep it's original docstring, use
from functools import wraps