In Python, decorators, let you quote, unquote 'decorate' a
function. But let's actually explore what that means.
Decorators are functions that wrap around your existing functions,
and we saw in the last video that you can nest functions,
and that's actually where this really comes into play
here, is this thing called a decorator.
So here's a scenario.
You've written a function, but you want to add some functionality
to it, so you can do one of two things really.
You can add changes to your existing function and potentially
break how it works down the road wherever else it's being
called because you've modified your function, or you can
write a new function that has the old code and modify it.
But with a decorator, it's sort of like being able to turn
extra features on and off.
All you have to do is call the decorator, and it will give it
extra features.
And every now and then, you know, sometimes you just want to add
those extra features to your base function,
and that's exactly what a decorator is for.
So a decorator typically you'll see it used with the '@' symbol,
and then it's decorator, 'decorator',
and then it goes around your 'function'.
This is what a decorator typically looks like.
And this can add more functionality to your function down
here, and this itself would be another function.
So all we're going to do is say, "Hey, there's a function called 'function()',
and just inject it into the decorator that we called 'decorator'.
And so we've essentially just nested a function inside of a function.
Now, to create a decorator, we're going to need to know about
function nesting,
and that was from the previous video.
So if you didn't watch that one, you're probably not going
to totally understand what's going on in this one.
So hop back a lesson if you didn't watch the video on nesting
functions. Otherwise, if you're ready, let's go ahead and
create our first decorator. So we can create a function in
here, and let's call it 'my_decorator',
and this decorator is going to take an 'original func'.
Now, in Python, you don't have to pass a strict variable,
you can also pass a function into another function.
And in here we're going to create another function,
it's a nested function,
and this one's just going to be called 'func_wrapper',
or it could even just be called 'wrapper'
if you want. 'print("Do a thing here")'.
And then it could run the 'original_func', and then it
could "Do a thing after",
And let's change 'here' to 'before'.
So "Do a thing before", run the function, and then, "Do a thing
after", and then in our main function called 'my_decorator',
we are simply going to return 'wrapper' as the entire function,
not as an executed function, but just as the function itself.
So let's go ahead and save that.
Let's create another function in here called 'greeting'.
And all this is going to say is 'print("HELLO WORLD")'.
So we're going to keep this very simple.
And if we run 'greeting' now, just says, "HELLO WORLD", the decorator
has not been applied.
We can apply this decorator though, in two different ways.
The first way, the most common way you're going to see, and
probably the most elegant way, is using the '@' symbol, '@
my_decorator', and that just matches the name here.
So if we rerun this cell, and rerun this cell, we can now
see "Do a thing before",
"HELLO WORLD", that's our function here,
"Do a thing after", and that's executing this code for us.
Now, that's how you create a decorator in less than five
minutes. There is, however, a second way to do this.
You could also say 'new_greeting =', and then you
could have 'my_decorator' as a function,
and you could also pass in 'greeting' as the function's parameter,
and now you can use 'new_greeting',
and if you don't use it like a function, you'll see this
typical thing here.
But if you do use it as a function, it will also work for you as well.
Actually, this is going to double work because this is still
decorated. So let's rerun these cells without the decorator,
and now we can see it works normally.
So here just looks like a regular function, and we're just
passing in a function instead of a regular variable.
What I like to do though, and maybe this is just because
I like writing a lot of Django,
and so I see decorators quite a bit.
I like 'my_decorator'.
I like it being above the function.
And at any point in time, you can just delete it, or you can
comment it out, and you can toggle on and off other features.
So let's go ahead and create a second dary decorator, something
that's going to actually do something, because right now
our decorator just prints stuff, and that's not actually useful,
and it doesn't actually show you that this is modifying anything.
It's just a 'print' statement, another 'print' statement, another 'print' statement.
And frankly, that's not what you're going to see in real
life Python code.
So let's create a new decorator, and I'm going to call it
'add_stuff', and it's going to take an 'original_func', and
then there's going to be a function inside of this.
We could call it 'wrapper', we could really call it anything.
It doesn't have to be 'wrapper', so it could be like the 'function_
wrapper', and due to scoping, we will have access to 'original_func' in here.
So we can run this right away.
We can run the 'original_func', and get that 'value'.
The 'value' of the 'original_func' is going to be not 'greeting',
'original_func',
then if we wanted to, we could actually return something like
an f-string where it says, "The original value was ",
or even better, let's not return that,
let's just print that,
and then let's change that 'value'.
So let's assume that this 'value' right now is going to be
a number just because we're going to be adding stuff.
So whatever that 'value' is, we're going to say 'value = value ** 2',
and we're going to 'return value'.
And let's go ahead, and just print out what the new 'value'
is. "The original value was ".
The new 'value' is going to be whatever 'value' to the power
of 2 is, and it's going to return that for us.
[no audio]
Let's take that function and return it here.
Okay, cool.
So now we have a decorator.
Let's go ahead and create a brand new function that simply
returns a number.
So we're gonna call this one 'def pie', and all it's going to
do is 'return 3.14'.
And yes, I know I spelt it 'pie', because I'm thinking
about 'pi' as a food right now.
And if we run 'pie', we see it's 3.14.
But if we take this decorator and throw it up here, run that
cell, and then rerun this cell,
"The original value was 3.14".
The new value is whatever 'pie' to the power of 2 is, which
is 9.8596 something rather, and it gives us the actual 'value' here.
Now, we can actually store this as well, because not only
are there 'print' statements in here, but it's returning a
'value' in which we can actually store into a variable.
So we could say 'pi_times_pi =', whatever that is,
and then 'pi_times_pi' as a variable is the actual output.
That's what we got from this 'return' statement here.
Now, in places like data science, you might not see this too often.
You might not even see this too often in machine learning
or artificial intelligence.
But I can tell you for certain in web development, in Django
and Flask, you're going to see these quite a bit.
And I actually really like this.
It just adds a little functionality.
And, hey, if you ever wanted to turn it off, you can just
comment it out or delete it.
Let's rerun those cells, and it goes back to normal.
Now, that's pretty cool.
Now, what I would like you to do is go ahead and try to create
your own decorator.
It does not need to do anything fancy.
And honestly, I'm going to suggest stick with something simple.
Just do a 'print' statement before, a 'print' statement after,
and run your function in here.
Give that a shot.
Don't forget, you can always download this Jupyter Notebook as well.