1
00:00:00,180 --> 00:00:01,410
In the last lesson,

2
00:00:01,470 --> 00:00:06,120
we had a primer on functions so that we can understand the next step

3
00:00:06,150 --> 00:00:07,920
which is the Python decorator.

4
00:00:08,430 --> 00:00:11,700
So we've seen that functions can have functionality, inputs,

5
00:00:11,700 --> 00:00:16,700
and outputs, functions are first-class objects and can be passed around as arguments,

6
00:00:17,280 --> 00:00:19,650
functions can be nested in other functions,

7
00:00:19,920 --> 00:00:24,900
and finally, functions can be returned as the output from another function

8
00:00:25,200 --> 00:00:29,700
without needing to trigger it. With these four things in mind,

9
00:00:29,790 --> 00:00:33,180
we're finally ready to tackle the Python decorator.

10
00:00:34,200 --> 00:00:34,950
First of all,

11
00:00:34,950 --> 00:00:39,950
I'm actually going to create a Python decorator for you so that we can talk

12
00:00:40,140 --> 00:00:41,460
about how it works.

13
00:00:42,060 --> 00:00:45,300
Here's what a Python decorator function looks like.

14
00:00:45,780 --> 00:00:49,590
We started off by creating just a normal function.

15
00:00:50,910 --> 00:00:53,580
Let's say I call it my decorator_function.

16
00:00:56,190 --> 00:01:00,000
This function is going to take another function as an input.

17
00:01:00,960 --> 00:01:05,760
Now, inside the decorator_functionn, I'm going to nest a wrapper_function.

18
00:01:07,530 --> 00:01:12,530
And this wrapper_function is then going to trigger the actual function that was

19
00:01:13,770 --> 00:01:16,440
passed in to the decorator function.

20
00:01:17,760 --> 00:01:22,020
And at the end of calling all the lines in my decorator_function,

21
00:01:22,290 --> 00:01:26,490
I'm going to return my wrapper_function. But remember

22
00:01:26,520 --> 00:01:30,090
without the parentheses. Now,

23
00:01:30,120 --> 00:01:33,750
basically what we've done is we've created a decorator_function

24
00:01:33,840 --> 00:01:38,840
which can do some stuff and it could control the calling

25
00:01:39,270 --> 00:01:40,860
of the function that was passed in.

26
00:01:41,550 --> 00:01:46,550
Remember that a decorator_function is just a function that wraps another

27
00:01:46,890 --> 00:01:51,390
function and gives that function some additional functionality.

28
00:01:51,810 --> 00:01:56,550
That's quite a mouthful, but let's take a look at an example.

29
00:01:57,390 --> 00:02:00,630
Let's say that I wanted to create a simple function

30
00:02:00,720 --> 00:02:02,880
which I'll call say_hello.

31
00:02:03,510 --> 00:02:06,360
Now this function is going to be the simplest form of function.

32
00:02:06,390 --> 00:02:09,000
It has no inputs and it has no outputs

33
00:02:09,270 --> 00:02:13,620
and all it does is just going to print hello. Now,

34
00:02:13,620 --> 00:02:17,280
what if I don't want to run this immediately

35
00:02:17,310 --> 00:02:21,630
the moment when I hit run, what if I wanted to add a delay to this function?

36
00:02:22,170 --> 00:02:25,980
Well, one way of doing that would be importing the time module

37
00:02:26,460 --> 00:02:28,680
and then before we print hello,

38
00:02:29,010 --> 00:02:32,520
we can go ahead and run time.sleep

39
00:02:32,850 --> 00:02:36,660
and we can sleep for a prespecified number of seconds.

40
00:02:37,290 --> 00:02:40,950
So now when I call this method, say_hello,

41
00:02:41,370 --> 00:02:45,420
and when I run it, you'll see that immediately nothing happens.

42
00:02:45,720 --> 00:02:49,440
And then after a two second delay, you can see the print

43
00:02:49,470 --> 00:02:52,290
hello actually gets triggered. Now,

44
00:02:52,290 --> 00:02:55,320
what if I wanted to create a whole bunch of functions

45
00:02:55,380 --> 00:02:58,080
like say_bye and say_greeting.

46
00:02:59,830 --> 00:03:04,510
And I wanted to add a delay onto each of these. Well,

47
00:03:04,510 --> 00:03:09,130
then I would have to copy and paste this code into all three places.

48
00:03:09,790 --> 00:03:12,010
Now, this is a very simple example,

49
00:03:12,280 --> 00:03:17,280
but frequently we'll want to add some additional piece of functionality to a

50
00:03:17,380 --> 00:03:22,270
number of our functions. So this is where the decorator comes in handy.

51
00:03:23,140 --> 00:03:27,850
What we could do is before we trigger the function that's passed in to

52
00:03:27,850 --> 00:03:31,420
the decorator_function, we can add the delay.

53
00:03:31,960 --> 00:03:36,960
So now whenever we decorate a function with this particular decorator_function,

54
00:03:38,590 --> 00:03:43,030
then it's going to delay for two seconds before it runs the function.

55
00:03:43,600 --> 00:03:48,010
So let's give this decorator function a little bit more of a descriptive name.

56
00:03:48,220 --> 00:03:51,940
Let's call it a delay_decorator. Well,

57
00:03:51,970 --> 00:03:56,970
now what we can do is we can simply use an @ sign

58
00:03:58,660 --> 00:04:03,660
and we can call the delay_decorator in front of the methods

59
00:04:05,320 --> 00:04:09,730
which we want to delay. So we can add it to all three of them,

60
00:04:09,760 --> 00:04:12,040
or we can only add it to some of them.

61
00:04:12,400 --> 00:04:17,050
But now when I actually run my function, say_hello

62
00:04:17,050 --> 00:04:20,649
for example, you can see that this one

63
00:04:20,649 --> 00:04:22,630
because it's got the delay decorator

64
00:04:22,900 --> 00:04:26,260
it's going to wait for two seconds before it prints hello.

65
00:04:26,650 --> 00:04:28,930
But if I call say_greeting

66
00:04:29,050 --> 00:04:31,900
which is the one that doesn't have the decorator above it,

67
00:04:32,260 --> 00:04:34,660
then you can see it gets triggered immediately.

68
00:04:35,350 --> 00:04:40,350
So this is a really nice way of adding something that you could do before you

69
00:04:40,600 --> 00:04:41,560
run the function,

70
00:04:42,360 --> 00:04:43,193
Right?

71
00:04:45,630 --> 00:04:50,630
or adding some functionality which you can do after the function. Or

72
00:04:51,510 --> 00:04:56,340
alternatively, you can modify the function so maybe you wanna run it twice.

73
00:04:56,910 --> 00:04:59,070
So now when I say hello,

74
00:05:01,950 --> 00:05:04,530
it's going to not only delay by two seconds,

75
00:05:04,800 --> 00:05:06,750
but it's also going to run it twice.

76
00:05:09,270 --> 00:05:14,220
This syntax is what we saw when we were creating our Flask

77
00:05:14,250 --> 00:05:19,250
app. It's got the @ sign and it basically decorates the function below with a

78
00:05:20,850 --> 00:05:22,050
decorator function.

79
00:05:22,080 --> 00:05:27,080
So putting this function through a machine that could modify the function or add

80
00:05:28,200 --> 00:05:32,400
some functionality before or after. Coming back to our definition,

81
00:05:32,820 --> 00:05:37,820
a decorator function is simply a function which wraps another function and gives

82
00:05:40,500 --> 00:05:44,640
it some additional functionality or modifies the functionality.

83
00:05:46,500 --> 00:05:51,390
Now, coming back to this syntax, this @ sign, it's

84
00:05:51,390 --> 00:05:54,150
what's known as syntactic sugar.

85
00:05:54,930 --> 00:05:59,930
It's some syntax that you can write to make it easier to write an alternative

86
00:06:01,430 --> 00:06:02,263
line of code.

87
00:06:02,810 --> 00:06:07,760
So let's say we wanted to add the decorator to this say_greeting. Well,

88
00:06:07,910 --> 00:06:11,450
if instead of using this syntactic sugar with the @ sign

89
00:06:11,450 --> 00:06:12,920
and then the name of the function,

90
00:06:13,280 --> 00:06:18,280
we could also just call the name of the decorator and then we could pass in the

91
00:06:20,570 --> 00:06:23,510
name of our function, say_greeting,

92
00:06:24,620 --> 00:06:29,620
and then we would end up with the decorated function as the output from this

93
00:06:31,310 --> 00:06:32,143
method.

94
00:06:32,660 --> 00:06:37,660
And we can now call the decorated function with the parentheses.

95
00:06:38,570 --> 00:06:43,310
So now we've triggered this say_greeting function

96
00:06:43,400 --> 00:06:45,860
and we've passed it through the delay decorator.

97
00:06:46,520 --> 00:06:51,520
But it's much easier to see at a glance what's going on by using this @ sign

98
00:06:51,980 --> 00:06:56,270
because you can see now you can just trigger this method by its own name

99
00:06:56,630 --> 00:07:00,020
and because the @delay_decorator is on top of it,

100
00:07:00,260 --> 00:07:02,540
then it's going to go through that filter.

101
00:07:03,950 --> 00:07:07,250
I recommend spending a few minutes looking at this code

102
00:07:07,520 --> 00:07:11,180
which you can access at this URL which is in the course resources,

103
00:07:11,660 --> 00:07:16,370
and then trying to recreate this functionality from scratch based on your

104
00:07:16,370 --> 00:07:20,360
understanding of what we just talked about. And then once you're done,

105
00:07:20,420 --> 00:07:23,510
when we head back to our Flask application

106
00:07:23,870 --> 00:07:27,890
you can now see that pretty much all of the code is easily understood.

107
00:07:28,340 --> 00:07:31,580
We know what this __name__ refers to,,

108
00:07:31,910 --> 00:07:34,160
we know that this is a decorator

109
00:07:34,400 --> 00:07:38,270
which is going to make sure that we only trigger this function

110
00:07:38,570 --> 00:07:41,840
if the user's trying to access the URL

111
00:07:42,020 --> 00:07:45,010
that is the homepage/.

112
00:07:46,160 --> 00:07:50,540
If we wanted to create another function, let's call it say_bye.

113
00:07:53,480 --> 00:07:56,840
This function could also get a decorator

114
00:07:57,350 --> 00:08:01,970
and this decorator will check to see well, if somebody goes to the URL 

115
00:08:01,970 --> 00:08:03,260
/bye, well

116
00:08:03,260 --> 00:08:07,160
then we're going to be triggering this method. Effectively

117
00:08:07,610 --> 00:08:12,610
the Flask framework is the one that determines which of these methods to call

118
00:08:14,180 --> 00:08:19,180
and these methods are only called when the decorator says that it's appropriate to do

119
00:08:19,820 --> 00:08:24,080
so. Now, if I go ahead and run this code once more,

120
00:08:26,390 --> 00:08:29,390
and we go to the URL that is our server,

121
00:08:29,720 --> 00:08:33,650
so this we know is the home route or the homepage,

122
00:08:33,980 --> 00:08:36,559
but if we go to /bye

123
00:08:36,919 --> 00:08:41,919
then we go to the bye route and this method gets rendered.

124
00:08:43,730 --> 00:08:47,330
Now in order to make sure that you get a chance writing decorator functions

125
00:08:47,330 --> 00:08:50,090
yourself, if you head over to the next lesson,

126
00:08:50,120 --> 00:08:53,720
I've got a coding exercise for you to challenge you

127
00:08:53,750 --> 00:08:55,760
to create your own decorator functions.

128
00:08:56,270 --> 00:08:59,540
And it's going to be a really useful decorator function because it's going to

129
00:08:59,540 --> 00:09:03,290
measure the amount of time that it takes a particular function to run.

130
00:09:03,920 --> 00:09:06,830
So for all of that and more, I'll see you on the next lesson.

