1. Decorators
Now that you know functions can be passed around as variables, and you understand scope and closures, we can talk about decorators.
2. Functions
So what is a decorator? Let's say you have a function that takes some inputs and returns some outputs.
3. Decorators
A decorator is a wrapper that you can place around a function that changes that function's behavior.
4. Modify inputs
You can modify the inputs,
5. Modify outputs
modify the outputs,
6. Modify function
or even change the behavior of the function itself.
7. What does a decorator look like?
You may have seen decorators in Python before. When you use them, you type the "@" symbol followed by the decorator's name on the line directly above the function you are decorating.
Here, the "double_args" decorator modifies the behavior of the multiply() function. double_args is a decorator that multiplies every argument by two before passing them to the decorated function. So 1 times 5 becomes 2 times 10, which equals 20.
That seems kind of magical that we can alter the behavior of functions, so let's peel back the layers and see how it works. We will build the double_args decorator together in this lesson.
8. The double_args decorator
Let's continue to use the multiply() function as the function we are decorating.
Now, decorators are just functions that take a function as an argument and return a modified version of that function. To start off, let's not have double_args modify anything. It just takes a function and immediately returns it.
If we call this version of double_args() that does nothing and pass it the multiply function and then assign the result to the variable "new_multiply", then we can call new_multiply(1, 5) and get the same value we would have gotten from multiply(1, 5).
9. The double_args decorator
In order for your decorator to return a modified function, it is usually helpful for it to define a new function to return. We'll call that nested function "wrapper()". All wrapper() does is take two arguments and passes them on to whatever function was passed to double_args() in the first place. If double_args() then returns the new wrapper() function, the return value acts exactly the same as whatever function was passed to double_args(), assuming that the function passed to double_args() also takes exactly two arguments.
So, double_args() is still not doing anything to actually modify the function it is decorating.
Once again, we'll pass multiply() to double_args() and assign the result to new_multiply(). If we then call new_multiply(), which is now equal to the wrapper() function, wrapper() calls multiply() because it is the function that was passed to double_args(). So wrapper() calls multiply() with the arguments 1 and 5, which returns 5.
10. The double_args decorator
Now let's actually modify the function our decorator is decorating. This time, wrapper() will still call whatever function is passed to double_args(), but it will double every argument when it calls the original function. See how it calls func() with a times 2 and b times 2?
As usual, we will call double_args() on the multiply() function and assign the result to new_multiply(). Now, what happens when we call new_multiply() with 1 and 5 as arguments? Well, new_multiply() is equal to wrapper(), which calls multiply() after doubling each argument. So 1 becomes 2 and 5 becomes 10, giving us 2 times 10, which equals 20.
11. The double_args decorator
We're almost there. This time, instead of assigning the new function to "new_multiply", we're going to overwrite the "multiply" variable.
Now calling multiply() with arguments 1 and 5 gives us 20 instead of 5.
Remember that we can do this because Python stores the original multiply function in the new function's closure.
12. Decorator syntax
When I first showed you the double_args() decorator at the beginning of this lecture, I used "@double_args" on the line before the definition of multiply(). This is just a Python convenience for saying "multiply" equals the value returned by calling double_args() with "multiply" as the only argument.
The code shown here on the left is exactly equivalent to the code on the right.
13. Let's practice!
And that's how decorators work! Let's practice this technique.