1 00:00:00,000 --> 00:00:03,800 In Python, decorators, let you quote, unquote 'decorate' a 2 00:00:03,800 --> 00:00:07,400 function. But let's actually explore what that means. 3 00:00:07,480 --> 00:00:11,480 Decorators are functions that wrap around your existing functions, 4 00:00:11,490 --> 00:00:14,180 and we saw in the last video that you can nest functions, 5 00:00:14,180 --> 00:00:16,700 and that's actually where this really comes into play 6 00:00:16,700 --> 00:00:18,300 here, is this thing called a decorator. 7 00:00:18,300 --> 00:00:19,700 So here's a scenario. 8 00:00:19,700 --> 00:00:23,380 You've written a function, but you want to add some functionality 9 00:00:23,390 --> 00:00:26,300 to it, so you can do one of two things really. 10 00:00:26,310 --> 00:00:30,020 You can add changes to your existing function and potentially 11 00:00:30,030 --> 00:00:32,840 break how it works down the road wherever else it's being 12 00:00:32,850 --> 00:00:36,250 called because you've modified your function, or you can 13 00:00:36,250 --> 00:00:40,500 write a new function that has the old code and modify it. 14 00:00:40,500 --> 00:00:43,700 But with a decorator, it's sort of like being able to turn 15 00:00:43,740 --> 00:00:45,420 extra features on and off. 16 00:00:45,420 --> 00:00:48,200 All you have to do is call the decorator, and it will give it 17 00:00:48,200 --> 00:00:49,200 extra features. 18 00:00:49,200 --> 00:00:52,500 And every now and then, you know, sometimes you just want to add 19 00:00:52,500 --> 00:00:54,800 those extra features to your base function, 20 00:00:54,860 --> 00:00:57,050 and that's exactly what a decorator is for. 21 00:00:57,060 --> 00:01:00,830 So a decorator typically you'll see it used with the '@' symbol, 22 00:01:00,840 --> 00:01:04,340 and then it's decorator, 'decorator', 23 00:01:04,599 --> 00:01:07,900 and then it goes around your 'function'. 24 00:01:07,900 --> 00:01:10,900 This is what a decorator typically looks like. 25 00:01:10,980 --> 00:01:14,550 And this can add more functionality to your function down 26 00:01:14,560 --> 00:01:17,430 here, and this itself would be another function. 27 00:01:17,430 --> 00:01:20,900 So all we're going to do is say, "Hey, there's a function called 'function()', 28 00:01:20,900 --> 00:01:24,200 and just inject it into the decorator that we called 'decorator'. 29 00:01:24,200 --> 00:01:28,100 And so we've essentially just nested a function inside of a function. 30 00:01:28,140 --> 00:01:31,170 Now, to create a decorator, we're going to need to know about 31 00:01:31,170 --> 00:01:32,400 function nesting, 32 00:01:32,400 --> 00:01:35,400 and that was from the previous video. 33 00:01:35,460 --> 00:01:39,120 So if you didn't watch that one, you're probably not going 34 00:01:39,130 --> 00:01:41,310 to totally understand what's going on in this one. 35 00:01:41,320 --> 00:01:45,390 So hop back a lesson if you didn't watch the video on nesting 36 00:01:45,400 --> 00:01:48,330 functions. Otherwise, if you're ready, let's go ahead and 37 00:01:48,340 --> 00:01:52,740 create our first decorator. So we can create a function in 38 00:01:52,740 --> 00:01:54,900 here, and let's call it 'my_decorator', 39 00:01:54,900 --> 00:01:58,400 and this decorator is going to take an 'original func'. 40 00:01:58,480 --> 00:02:01,750 Now, in Python, you don't have to pass a strict variable, 41 00:02:01,750 --> 00:02:05,600 you can also pass a function into another function. 42 00:02:06,060 --> 00:02:10,150 And in here we're going to create another function, 43 00:02:10,160 --> 00:02:11,770 it's a nested function, 44 00:02:12,100 --> 00:02:16,600 and this one's just going to be called 'func_wrapper', 45 00:02:16,630 --> 00:02:18,700 or it could even just be called 'wrapper' 46 00:02:18,710 --> 00:02:23,230 if you want. 'print("Do a thing here")'. 47 00:02:23,440 --> 00:02:28,780 And then it could run the 'original_func', and then it 48 00:02:28,780 --> 00:02:32,300 could "Do a thing after", 49 00:02:32,300 --> 00:02:34,200 And let's change 'here' to 'before'. 50 00:02:34,250 --> 00:02:37,210 So "Do a thing before", run the function, and then, "Do a thing 51 00:02:37,220 --> 00:02:42,630 after", and then in our main function called 'my_decorator', 52 00:02:42,640 --> 00:02:46,380 we are simply going to return 'wrapper' as the entire function, 53 00:02:46,500 --> 00:02:51,800 not as an executed function, but just as the function itself. 54 00:02:51,800 --> 00:02:53,400 So let's go ahead and save that. 55 00:02:53,400 --> 00:02:57,200 Let's create another function in here called 'greeting'. 56 00:02:57,200 --> 00:03:01,200 And all this is going to say is 'print("HELLO WORLD")'. 57 00:03:01,200 --> 00:03:02,400 So we're going to keep this very simple. 58 00:03:03,240 --> 00:03:08,700 And if we run 'greeting' now, just says, "HELLO WORLD", the decorator 59 00:03:08,700 --> 00:03:09,700 has not been applied. 60 00:03:09,700 --> 00:03:12,900 We can apply this decorator though, in two different ways. 61 00:03:12,920 --> 00:03:16,340 The first way, the most common way you're going to see, and 62 00:03:16,350 --> 00:03:19,910 probably the most elegant way, is using the '@' symbol, '@ 63 00:03:20,000 --> 00:03:23,500 my_decorator', and that just matches the name here. 64 00:03:23,500 --> 00:03:28,100 So if we rerun this cell, and rerun this cell, we can now 65 00:03:28,100 --> 00:03:29,500 see "Do a thing before", 66 00:03:29,500 --> 00:03:31,800 "HELLO WORLD", that's our function here, 67 00:03:31,800 --> 00:03:35,800 "Do a thing after", and that's executing this code for us. 68 00:03:35,800 --> 00:03:38,560 Now, that's how you create a decorator in less than five 69 00:03:38,570 --> 00:03:40,840 minutes. There is, however, a second way to do this. 70 00:03:40,980 --> 00:03:45,930 You could also say 'new_greeting =', and then you 71 00:03:45,930 --> 00:03:50,000 could have 'my_decorator' as a function, 72 00:03:50,000 --> 00:03:54,320 and you could also pass in 'greeting' as the function's parameter, 73 00:03:55,210 --> 00:03:58,820 and now you can use 'new_greeting', 74 00:03:58,830 --> 00:04:01,750 and if you don't use it like a function, you'll see this 75 00:04:01,760 --> 00:04:02,740 typical thing here. 76 00:04:02,740 --> 00:04:08,200 But if you do use it as a function, it will also work for you as well. 77 00:04:08,240 --> 00:04:11,810 Actually, this is going to double work because this is still 78 00:04:11,820 --> 00:04:14,600 decorated. So let's rerun these cells without the decorator, 79 00:04:14,600 --> 00:04:16,700 and now we can see it works normally. 80 00:04:16,760 --> 00:04:20,149 So here just looks like a regular function, and we're just 81 00:04:20,149 --> 00:04:22,300 passing in a function instead of a regular variable. 82 00:04:22,370 --> 00:04:25,370 What I like to do though, and maybe this is just because 83 00:04:25,370 --> 00:04:26,899 I like writing a lot of Django, 84 00:04:26,899 --> 00:04:29,400 and so I see decorators quite a bit. 85 00:04:29,900 --> 00:04:32,300 I like 'my_decorator'. 86 00:04:32,900 --> 00:04:35,300 I like it being above the function. 87 00:04:35,300 --> 00:04:37,990 And at any point in time, you can just delete it, or you can 88 00:04:38,000 --> 00:04:41,600 comment it out, and you can toggle on and off other features. 89 00:04:41,650 --> 00:04:45,970 So let's go ahead and create a second dary decorator, something 90 00:04:45,980 --> 00:04:48,970 that's going to actually do something, because right now 91 00:04:48,980 --> 00:04:51,700 our decorator just prints stuff, and that's not actually useful, 92 00:04:51,710 --> 00:04:55,130 and it doesn't actually show you that this is modifying anything. 93 00:04:55,130 --> 00:04:58,700 It's just a 'print' statement, another 'print' statement, another 'print' statement. 94 00:04:58,700 --> 00:05:01,500 And frankly, that's not what you're going to see in real 95 00:05:01,500 --> 00:05:02,700 life Python code. 96 00:05:02,780 --> 00:05:06,740 So let's create a new decorator, and I'm going to call it 97 00:05:06,750 --> 00:05:13,240 'add_stuff', and it's going to take an 'original_func', and 98 00:05:13,250 --> 00:05:15,340 then there's going to be a function inside of this. 99 00:05:15,400 --> 00:05:18,700 We could call it 'wrapper', we could really call it anything. 100 00:05:18,710 --> 00:05:21,770 It doesn't have to be 'wrapper', so it could be like the 'function_ 101 00:05:21,770 --> 00:05:26,300 wrapper', and due to scoping, we will have access to 'original_func' in here. 102 00:05:26,360 --> 00:05:28,520 So we can run this right away. 103 00:05:28,520 --> 00:05:31,400 We can run the 'original_func', and get that 'value'. 104 00:05:31,720 --> 00:05:34,750 The 'value' of the 'original_func' is going to be not 'greeting', 105 00:05:34,940 --> 00:05:36,470 'original_func', 106 00:05:36,480 --> 00:05:39,260 then if we wanted to, we could actually return something like 107 00:05:39,260 --> 00:05:46,000 an f-string where it says, "The original value was ", 108 00:05:46,620 --> 00:05:49,020 or even better, let's not return that, 109 00:05:49,020 --> 00:05:50,400 let's just print that, 110 00:05:50,400 --> 00:05:52,800 and then let's change that 'value'. 111 00:05:52,800 --> 00:05:54,800 So let's assume that this 'value' right now is going to be 112 00:05:54,800 --> 00:05:56,680 a number just because we're going to be adding stuff. 113 00:05:56,680 --> 00:06:03,000 So whatever that 'value' is, we're going to say 'value = value ** 2', 114 00:06:03,040 --> 00:06:06,090 and we're going to 'return value'. 115 00:06:06,100 --> 00:06:11,160 And let's go ahead, and just print out what the new 'value' 116 00:06:11,170 --> 00:06:12,870 is. "The original value was ". 117 00:06:12,880 --> 00:06:15,570 The new 'value' is going to be whatever 'value' to the power 118 00:06:15,570 --> 00:06:17,600 of 2 is, and it's going to return that for us. 119 00:06:17,600 --> 00:06:21,800 [no audio] 120 00:06:21,800 --> 00:06:26,530 Let's take that function and return it here. 121 00:06:26,540 --> 00:06:27,760 Okay, cool. 122 00:06:27,910 --> 00:06:29,620 So now we have a decorator. 123 00:06:29,620 --> 00:06:32,960 Let's go ahead and create a brand new function that simply 124 00:06:32,970 --> 00:06:33,950 returns a number. 125 00:06:33,960 --> 00:06:38,790 So we're gonna call this one 'def pie', and all it's going to 126 00:06:38,800 --> 00:06:41,100 do is 'return 3.14'. 127 00:06:41,190 --> 00:06:44,460 And yes, I know I spelt it 'pie', because I'm thinking 128 00:06:44,460 --> 00:06:46,200 about 'pi' as a food right now. 129 00:06:46,220 --> 00:06:49,040 And if we run 'pie', we see it's 3.14. 130 00:06:50,500 --> 00:06:56,100 But if we take this decorator and throw it up here, run that 131 00:06:56,100 --> 00:06:58,500 cell, and then rerun this cell, 132 00:06:58,560 --> 00:07:00,780 "The original value was 3.14". 133 00:07:00,790 --> 00:07:04,730 The new value is whatever 'pie' to the power of 2 is, which 134 00:07:04,730 --> 00:07:09,100 is 9.8596 something rather, and it gives us the actual 'value' here. 135 00:07:09,160 --> 00:07:12,190 Now, we can actually store this as well, because not only 136 00:07:12,200 --> 00:07:15,330 are there 'print' statements in here, but it's returning a 137 00:07:15,330 --> 00:07:17,900 'value' in which we can actually store into a variable. 138 00:07:17,900 --> 00:07:22,900 So we could say 'pi_times_pi =', whatever that is, 139 00:07:22,900 --> 00:07:28,600 and then 'pi_times_pi' as a variable is the actual output. 140 00:07:28,600 --> 00:07:31,000 That's what we got from this 'return' statement here. 141 00:07:31,000 --> 00:07:35,500 Now, in places like data science, you might not see this too often. 142 00:07:35,500 --> 00:07:38,710 You might not even see this too often in machine learning 143 00:07:38,710 --> 00:07:40,000 or artificial intelligence. 144 00:07:40,000 --> 00:07:44,950 But I can tell you for certain in web development, in Django 145 00:07:44,950 --> 00:07:47,800 and Flask, you're going to see these quite a bit. 146 00:07:49,000 --> 00:07:50,200 And I actually really like this. 147 00:07:50,290 --> 00:07:51,750 It just adds a little functionality. 148 00:07:51,760 --> 00:07:55,210 And, hey, if you ever wanted to turn it off, you can just 149 00:07:55,220 --> 00:07:56,650 comment it out or delete it. 150 00:07:56,660 --> 00:08:00,500 Let's rerun those cells, and it goes back to normal. 151 00:08:00,500 --> 00:08:01,800 Now, that's pretty cool. 152 00:08:01,840 --> 00:08:05,680 Now, what I would like you to do is go ahead and try to create 153 00:08:05,690 --> 00:08:06,790 your own decorator. 154 00:08:06,800 --> 00:08:08,650 It does not need to do anything fancy. 155 00:08:08,660 --> 00:08:11,890 And honestly, I'm going to suggest stick with something simple. 156 00:08:11,900 --> 00:08:14,880 Just do a 'print' statement before, a 'print' statement after, 157 00:08:14,880 --> 00:08:16,900 and run your function in here. 158 00:08:16,960 --> 00:08:18,160 Give that a shot. 159 00:08:18,160 --> 00:08:22,900 Don't forget, you can always download this Jupyter Notebook as well.