1
00:00:05,360 --> 00:00:08,880
We left the previous video with three different ways to create the same output,

2
00:00:08,950 --> 00:00:11,770
and the question: "Which way performs better?".

3
00:00:11,770 --> 00:00:14,520
So the code's in compchallenge2b.py

4
00:00:14,740 --> 00:00:20,200
But before we start comparing how fast the three approaches are, we should make sure we're comparing

5
00:00:20,440 --> 00:00:21,810
like for like.

6
00:00:21,880 --> 00:00:28,840
And in this case we're not. Now our "nested for loops" creates six lists, and discards each list before creating

7
00:00:28,840 --> 00:00:29,580
the next one.

8
00:00:29,650 --> 00:00:34,450
And you can see, here on line 18, that new list being created each time through the loop.

9
00:00:34,450 --> 00:00:39,670
Now that "List comprehension inside a for loop", this next section of code, that does the same thing each

10
00:00:39,670 --> 00:00:40,420
er each

11
00:00:40,450 --> 00:00:44,240
time around the loop, the comprehension assigns a new list to

12
00:00:44,240 --> 00:00:47,200
exits_to_destination_2

13
00:00:47,300 --> 00:00:48,970
You can say that happening on line 30.

14
00:00:49,300 --> 00:00:55,910
And thirdly, the "nested comprehension" builds up a nested list containing all 6 of the other lists.

15
00:00:55,980 --> 00:00:59,350
This is the code down here, starting at line 36.

16
00:00:59,410 --> 00:01:05,200
So before we can perform any meaningful analysis, we need to decide what we're trying to measure here.

17
00:01:05,440 --> 00:01:10,630
Now if we want to use the lists somewhere else in our code, then we're really interested in how efficiently

18
00:01:10,690 --> 00:01:16,680
the lists can be created - and only the third method is creating the complete list here.

19
00:01:17,080 --> 00:01:22,170
But on the other hand, if we're only interested in displaying the result, then we can measure the code

20
00:01:22,170 --> 00:01:23,840
as it is at the moment.

21
00:01:23,830 --> 00:01:26,040
Now as we're learning about all this we're going to do both

22
00:01:26,040 --> 00:01:31,810
here. We'll start by measuring how quickly the results can be displayed, because that's what the code's doing

23
00:01:31,810 --> 00:01:32,530
at the moment.

24
00:01:32,710 --> 00:01:36,550
But there is one change we have to make, to keep the comparison fair.

25
00:01:36,550 --> 00:01:39,710
So our nested loop is printing the list twice.

26
00:01:39,790 --> 00:01:45,670
So I'm going to start here by looking at line 40, and actually delete those three lines. So printing out the

27
00:01:45,670 --> 00:01:47,460
exits to Destination 3.

28
00:01:47,740 --> 00:01:54,760
And also that print there as well. So I'm actually going to delete those lines. Like so. And that's just again to make

29
00:01:54,760 --> 00:01:57,690
sure that we're keeping the comparisons fair here.

30
00:01:57,910 --> 00:02:00,640
All right so let's have a look at the timeit module.

31
00:02:01,120 --> 00:02:02,030
I'll open up a browser.

32
00:02:07,250 --> 00:02:08,949
This is a good place to start.

33
00:02:08,960 --> 00:02:13,510
Now if you want to run tests from the command line, the documentation shows how to use the timeit

34
00:02:13,510 --> 00:02:14,880
module that way.

35
00:02:14,990 --> 00:02:20,330
So that might be fine for testing a single line Python statement, but it gets increasingly complex when

36
00:02:20,330 --> 00:02:23,320
testing larger or longer blocks of code.

37
00:02:23,330 --> 00:02:28,730
Remember the indentation's vitally important in Python, which means you need to start multiple strings

38
00:02:28,820 --> 00:02:31,390
with the correct number of spaces.

39
00:02:31,400 --> 00:02:36,110
So by all means experiment with a command line interface if you want, but we won't be doing things that

40
00:02:36,110 --> 00:02:36,700
way.

41
00:02:37,040 --> 00:02:43,370
So to use timeit in our code, we have to import the timeit module, then either use the timeit function or

42
00:02:43,370 --> 00:02:45,870
create an instance of the Timer class.

43
00:02:45,890 --> 00:02:51,080
Now the timeit function is one of three convenience functions that make use of the Timer class a bit

44
00:02:51,080 --> 00:02:51,750
easier.

45
00:02:52,010 --> 00:02:54,490
Although there's just not much difference in the code you have to write.

46
00:02:54,590 --> 00:02:59,290
So I'm going to use the timeit function, because printing the result is slightly easier.

47
00:02:59,660 --> 00:03:08,290
So coming down here to 27.5.2, the timeit function is passed a statement to

48
00:03:08,290 --> 00:03:12,870
execute. And we're going to start off by passing a statement as a string,

49
00:03:13,000 --> 00:03:15,320
but you can also pass a function instead.

50
00:03:15,370 --> 00:03:18,220
As long as the function doesn't take any arguments.

51
00:03:18,600 --> 00:03:19,750
So that's the first one.

52
00:03:19,770 --> 00:03:25,570
The first statement argument there. The next one is a uh the next parameter is a string containing

53
00:03:25,570 --> 00:03:26,310
setup code.

54
00:03:26,360 --> 00:03:28,200
We're going to come back to that in a moment.

55
00:03:28,210 --> 00:03:34,620
Now the default timer, you can see over here, the third argument, that can be changed if you feel the need to.

56
00:03:34,690 --> 00:03:37,670
Although there is very rarely any need to use a different timer.

57
00:03:38,140 --> 00:03:44,320
Now we looked at the time.perf_counter function in an earlier section, when we were investigating

58
00:03:44,320 --> 00:03:49,180
the time module. That's now the default timer, since Python 3.3 and as I said, there's

59
00:03:49,180 --> 00:03:51,090
rarely any need to change that.

60
00:03:51,180 --> 00:03:56,110
Now this fourth argument, number, is how many times to execute the code.

61
00:03:56,170 --> 00:04:00,970
Now the default value is, as you can see there, is 1 million; which means your code will be executed one million

62
00:04:00,970 --> 00:04:02,120
times.

63
00:04:02,140 --> 00:04:05,530
Now that might seem like a lot, but it does give good results.

64
00:04:05,860 --> 00:04:09,510
But one thing we probably shouldn't do though, is leave it set to the default

65
00:04:09,520 --> 00:04:14,500
when our code prints things out. Now at the moment, each of our methods prints the lists, and printing is

66
00:04:14,500 --> 00:04:16,120
quite a slow operation.

67
00:04:16,120 --> 00:04:20,700
Now if we leave the default number there, it will take about 1 minute to execute each block of code 1

68
00:04:20,709 --> 00:04:22,079
million times.

69
00:04:22,150 --> 00:04:28,260
And lastly this global argument, the final parameter, that's used to specify the namespace that our code

70
00:04:28,270 --> 00:04:32,820
will run in. Now I'm going to come back to that when we look at the setup string, because it's easier to explain

71
00:04:32,830 --> 00:04:35,950
by seeing an example. Alright, so that's the timeit function.

72
00:04:35,950 --> 00:04:37,690
Let's get back to our code.

73
00:04:38,430 --> 00:04:43,270
Let's start setting this up, so first things that we need to do is import the timeit module.

74
00:04:43,570 --> 00:04:44,950
So we'll do that, obviously, on line one

75
00:04:47,780 --> 00:04:51,300
Ok, so import timeit.

76
00:04:51,610 --> 00:04:57,030
Right, so at this point we've now got a choice; and which one you choose to do is entirely up to you.

77
00:04:57,050 --> 00:05:02,540
Now we can either turn our three sections of code into functions or we can wrap them in quotes to turn

78
00:05:02,540 --> 00:05:03,830
them into strings.

79
00:05:03,830 --> 00:05:08,400
Now I'm going to use strings, because turning each code block into a function's easy.

80
00:05:08,510 --> 00:05:11,960
Now there's a couple of things to watch out for when using strings.

81
00:05:11,990 --> 00:05:14,100
First, use triple quotes.

82
00:05:14,270 --> 00:05:18,260
That way any reformatting of your code can be kept to a minimum.

83
00:05:18,290 --> 00:05:19,760
So let's go down and start running some code.

84
00:05:19,760 --> 00:05:22,280
I'm going to call the first string nested_loop.

85
00:05:22,460 --> 00:05:35,440
So I'm goint to start by putting some code here, on line 19. So we've got nested_loop equals and then three quotes and backslash.

86
00:05:35,550 --> 00:05:43,210
Then I'm going to come down here, to line 27, and I put the three quotes again. Like so. And again, notice

87
00:05:43,250 --> 00:05:47,250
the backslash at the end of the opening quotes, here on line 19.

88
00:05:47,380 --> 00:05:52,420
We don't really want our code to start with a blank line, and the line continuation character avoids

89
00:05:52,420 --> 00:05:53,290
that.

90
00:05:53,290 --> 00:05:58,330
Now be careful when typing in code, rather than converting existing code to a string.

91
00:05:58,330 --> 00:05:59,710
The code must start with

92
00:05:59,720 --> 00:06:04,910
no indentation, then be correctly indented relative to the first line.

93
00:06:04,990 --> 00:06:11,440
Incorrect indentation causes an error when timeit tries to execute the code, but because it's a string

94
00:06:11,800 --> 00:06:13,750
you won't get any help from your IDE.

95
00:06:14,090 --> 00:06:14,370
OK.

96
00:06:14,410 --> 00:06:16,490
So that first bit, that's fairly straightforward.

97
00:06:16,510 --> 00:06:19,980
We've now got a string containing our first block of code.

98
00:06:20,260 --> 00:06:23,580
So we want to do the same thing with the other bits of code that we want to use in our test.

99
00:06:23,920 --> 00:06:29,320
So for the "list comprehension inside a for loop" then it'll go below the prints there.

100
00:06:29,730 --> 00:06:37,420
Well call it loop_comp equals, three double quotes again, and a backslash.

101
00:06:37,420 --> 00:06:41,570
Then we go down to just before the print and we put three double quotes.

102
00:06:42,180 --> 00:06:43,580
OK so that's our second one.

103
00:06:43,780 --> 00:06:44,980
And our third one, for the nested

104
00:06:44,990 --> 00:06:48,100
comprehension, below the two prints again.

105
00:06:48,430 --> 00:06:56,170
We're going to do nested_comp equals three double quotes again, backslash, just scroll down

106
00:06:56,260 --> 00:06:57,180
a little bit.

107
00:06:57,400 --> 00:07:01,400
Then we want to do three double quotes after that for loop.

108
00:07:02,410 --> 00:07:09,280
OK. So we've now, at this point, got three strings, that we can pass to the timeit function to see how their performance

109
00:07:09,280 --> 00:07:10,700
compares.

110
00:07:10,750 --> 00:07:11,840
Now we're going to get an error.

111
00:07:11,860 --> 00:07:15,060
But I'm going to go ahead anyway, because it's a good way to explain about

112
00:07:15,070 --> 00:07:19,710
those setup and globals parameters, that we saw in the documentation earlier.

113
00:07:19,770 --> 00:07:24,920
So what I'm going to do is, on line 50, we're going to put a bit of code in here.

114
00:07:24,950 --> 00:07:27,400
I'm going to put or type, result_1

115
00:07:27,400 --> 00:07:34,800
result_1 is equal to timeit.timeit and in parentheses we're going to put nested_loop.

116
00:07:38,230 --> 00:07:47,470
And then we're going to do print("Nested loop colon backslash T for a tab character,

117
00:07:47,500 --> 00:07:55,120
left and right curly braces, ending double quote, dot format and in parentheses result_1, and closing

118
00:07:55,120 --> 00:07:59,230
off the two parentheses. Blank line on line 52.

119
00:07:59,500 --> 00:08:00,670
So if we now run this

120
00:08:05,450 --> 00:08:06,560
Scroll up and have a look at the top

121
00:08:10,310 --> 00:08:17,210
and you can see we've got some errors, but right down at the bottom we've got this NameError: name 'locations' is not defined.

122
00:08:17,240 --> 00:08:23,130
Now that seems strange, because we can clearly see our locations dictionary, if we go up to the top here

123
00:08:23,290 --> 00:08:26,620
on line 3, that's been defined, and it is at the top of the file.

124
00:08:26,900 --> 00:08:32,720
But the problem is that timeit isn't aware of anything defined in your program, except for the arguments

125
00:08:32,720 --> 00:08:34,600
we pass to it, of course.

126
00:08:34,610 --> 00:08:40,370
So that's why it provides the setup and globals parameters, to allow us to set up the environment that

127
00:08:40,370 --> 00:08:41,960
our code will execute in.

128
00:08:41,960 --> 00:08:47,850
Now I'd normally use setup when possible, as it allows you to be more specific about what you pass

129
00:08:47,850 --> 00:08:48,370
to it.

130
00:08:48,630 --> 00:08:53,330
So I'm going to start with globals here though, because it's a simple change to the code. And once you've seen

131
00:08:53,330 --> 00:08:58,130
that working, I'll change the code to provide the dictionaries in the setup argument.

132
00:08:58,230 --> 00:09:00,980
We've already seen how to get the global variables for our modules,

133
00:09:01,070 --> 00:09:03,380
earlier in the course, using the globals() function.

134
00:09:03,380 --> 00:09:09,930
So we can pass the result of calling globals() as the globals argument to timeit. so I'm going to do that, and go down to

135
00:09:09,930 --> 00:09:12,090
line 50 again. I'll close the Run Window first

136
00:09:14,930 --> 00:09:19,610
And here, what we're going to do, on line 50, we'll actually pass that. So we're on the next line

137
00:09:19,610 --> 00:09:28,100
after nested_loop comma globals equals, then a call to the globals function: globals().

138
00:09:28,360 --> 00:09:34,610
So our code snippet will now execute in our global namespace, which means that everything defined in our module

139
00:09:34,760 --> 00:09:36,860
will be available to the snippet.

140
00:09:37,040 --> 00:09:42,760
Now, that could well be overkill, for testing a small snippet of code, but it can be useful if the environment's

141
00:09:42,770 --> 00:09:45,710
too complex to set up in a small block of code.

142
00:09:46,100 --> 00:09:50,930
So there's another reason I prefer not to use globals to set the namespace, and that's because it was

143
00:09:50,930 --> 00:09:56,730
introduced in Python 3.5, and so therefore it won't work in earlier versions.

144
00:09:57,300 --> 00:10:01,200
So what we've done here will work, but I'm not going to run it right away.

145
00:10:01,640 --> 00:10:05,810
And that's because the default number of times for timeit is 1 million runs of the snippet, which we

146
00:10:05,810 --> 00:10:08,850
talked about earlier, and that would take about a minute to finish.

147
00:10:09,020 --> 00:10:10,900
And that's a long time to stare at the screen.

148
00:10:10,910 --> 00:10:14,550
I'm going to reduce the number to 1000 before running the program.

149
00:10:14,650 --> 00:10:23,980
So to do that, we're going to put a comma after the parentheses, and number=1000.

150
00:10:24,000 --> 00:10:27,140
Now just to be clear, I could've left that set to the default.

151
00:10:27,270 --> 00:10:29,160
And you'd normally want to do that.

152
00:10:29,250 --> 00:10:32,950
I just don't want to spend a minute in silence on the video, while we wait for it to finish.

153
00:10:33,290 --> 00:10:33,900
Let's run that

154
00:10:38,050 --> 00:10:43,500
And you can see, down the bottom, that it's printed out the time it took for our snippet to be executed 1000 times.

155
00:10:43,540 --> 00:10:46,230
Now the exact time will depend on your processor speed,

156
00:10:46,240 --> 00:10:51,720
how much memory your computer's got, and all sorts of other factors. Python version, for example. And if you've

157
00:10:51,730 --> 00:10:56,330
got other programs running, that will also have an impact on how quickly the code executes.

158
00:10:56,540 --> 00:11:01,150
It's likely that your computer's streaming this video, and doing that will certainly be taking up a fair

159
00:11:01,150 --> 00:11:03,340
bit of your processor's time as well.

160
00:11:03,540 --> 00:11:08,580
Now my Mac's busy recording this video, at the same time as I'm performing these timing tests.

161
00:11:08,620 --> 00:11:12,360
So these aren't really ideal conditions to be doing this, for either of us.

162
00:11:12,430 --> 00:11:16,960
I'd be very surprised, frankly, if you got the exact same value as me; but that's actually not what the

163
00:11:16,960 --> 00:11:18,390
timeit module's for.

164
00:11:18,580 --> 00:11:25,290
It's used to compare different code snippets, not to work out exactly how long your code took to execute. So

165
00:11:25,350 --> 00:11:30,280
running different snippets on the same computer will give a good indication of which one is the fastest.

166
00:11:30,640 --> 00:11:36,760
But be aware of what else your computer is doing at the same time, and ideally close down any other programs

167
00:11:36,760 --> 00:11:42,060
that aren't essential to the test. Alright, so let's finish the video here. In the next video,

168
00:11:42,060 --> 00:11:47,400
We'll start looking at that setup argument, which I talked about as an alternative, to fix that error we got

169
00:11:47,400 --> 00:11:48,460
with locations.

170
00:11:48,630 --> 00:11:50,250
So I'll see you in the next video.

