1
00:00:04,740 --> 00:00:06,839
In this video, we're going to have a look

2
00:00:06,839 --> 00:00:09,539
at how the any and all functions can be

3
00:00:09,539 --> 00:00:12,209
useful, when used with a comprehension

4
00:00:12,209 --> 00:00:15,299
or generator expression. So let's start by

5
00:00:15,299 --> 00:00:20,730
creating a new python file, and we'll

6
00:00:20,730 --> 00:00:22,320
go with calling this one

7
00:00:22,320 --> 00:00:30,600
anycomprehension.py. So for the first

8
00:00:30,600 --> 00:00:32,549
example, let's say we're writing an email

9
00:00:32,549 --> 00:00:34,949
client in Python. I'm going to start on

10
00:00:34,949 --> 00:00:37,460
the first line - I'm typing from data

11
00:00:37,460 --> 00:00:43,320
import people basic plants underscore list, or

12
00:00:43,320 --> 00:00:45,210
underscore plants underscore list, and as

13
00:00:45,210 --> 00:00:48,870
well as plants underscore list. So as I

14
00:00:48,870 --> 00:00:50,790
mentioned, we're going to assume we're

15
00:00:50,790 --> 00:00:52,800
writing an email client in Python. We've

16
00:00:52,800 --> 00:00:54,420
allowed the user to choose the people

17
00:00:54,420 --> 00:00:56,730
who receive the email from a contacts

18
00:00:56,730 --> 00:00:58,980
database, maybe. The user wants to send

19
00:00:58,980 --> 00:01:01,020
the email and the first thing we might

20
00:01:01,020 --> 00:01:03,239
want to do is check that all the people

21
00:01:03,239 --> 00:01:05,280
have an email address. If they do we can

22
00:01:05,280 --> 00:01:07,320
attempt to send the email. If they don't

23
00:01:07,320 --> 00:01:09,450
we let the user edit the list of

24
00:01:09,450 --> 00:01:11,640
recipients. To do that we need to check

25
00:01:11,640 --> 00:01:13,860
the second field of each of the people.

26
00:01:13,860 --> 00:01:15,810
We can create a list containing that

27
00:01:15,810 --> 00:01:18,659
field by using a list comprehension. So

28
00:01:18,659 --> 00:01:19,830
let's go ahead and do that. So on line

29
00:01:19,830 --> 00:01:22,939
3 I'm going to type if all

30
00:01:22,939 --> 00:01:26,580
parentheses square bracket person

31
00:01:26,580 --> 00:01:28,710
left square bracket 1 right

32
00:01:28,710 --> 00:01:30,750
square bracket space and its for

33
00:01:30,750 --> 00:01:34,830
person in people, then we're closing

34
00:01:34,830 --> 00:01:38,520
right square bracket there, right

35
00:01:38,520 --> 00:01:42,439
parenthesis and then a colon. Then print

36
00:01:42,439 --> 00:01:46,820
parentheses double quote Sending email,

37
00:01:46,820 --> 00:01:49,049
closing double quote closing parenthesis.

38
00:01:49,049 --> 00:01:50,970
Then on the next line, we'll go back into

39
00:01:50,970 --> 00:01:54,450
an else colon, and print on the line

40
00:01:54,450 --> 00:01:57,240
after that, double quotes in parentheses

41
00:01:57,240 --> 00:02:03,320
User must edit the list of recipients,

42
00:02:03,320 --> 00:02:06,689
double quote right curly brace, or right

43
00:02:06,689 --> 00:02:08,579
parenthesis, I should say. So the

44
00:02:08,579 --> 00:02:11,790
all function will return true if all of the

45
00:02:11,790 --> 00:02:14,129
email addresses aren't empty, false if one

46
00:02:14,129 --> 00:02:16,290
or more of them are empty. So if we run

47
00:02:16,290 --> 00:02:18,340
the program now,

48
00:02:18,340 --> 00:02:20,810
you can see the user must edit the list

49
00:02:20,810 --> 00:02:23,060
of recipients, and that's exactly what we

50
00:02:23,060 --> 00:02:26,120
want, or returns false because a

51
00:02:26,120 --> 00:02:27,560
couple of the people are missing an email

52
00:02:27,560 --> 00:02:30,650
address. If we go back to our data dot

53
00:02:30,650 --> 00:02:37,790
py, and you can see that Eric Idle on

54
00:02:37,790 --> 00:02:39,920
line 6 and Michael Palin on line 9

55
00:02:39,920 --> 00:02:41,689
haven't got email addresses, which

56
00:02:41,689 --> 00:02:44,239
is consistent with our code failing,

57
00:02:44,239 --> 00:02:46,280
or returning false I should say, for that

58
00:02:46,280 --> 00:02:49,219
reason. So as you can see, using all is

59
00:02:49,219 --> 00:02:51,319
much simpler than iterating over the

60
00:02:51,319 --> 00:02:53,930
list, checking each email address. Another

61
00:02:53,930 --> 00:02:56,090
advantage of all, and any too for that

62
00:02:56,090 --> 00:02:58,189
matter, is that they short-circuit the

63
00:02:58,189 --> 00:03:01,099
evaluation of the iterable. As soon as

64
00:03:01,099 --> 00:03:02,090
the result is known,

65
00:03:02,090 --> 00:03:04,189
they stop checking any more values. In

66
00:03:04,189 --> 00:03:06,949
the case of all, it can return false as

67
00:03:06,949 --> 00:03:09,260
soon as the false value is found. In the

68
00:03:09,260 --> 00:03:11,510
case of any, it can return true as soon

69
00:03:11,510 --> 00:03:13,090
as a true value is found.

70
00:03:13,090 --> 00:03:15,590
Remember that gotcha from a few videos

71
00:03:15,590 --> 00:03:18,560
ago, though? The code will fail if the

72
00:03:18,560 --> 00:03:20,750
list is empty, and we can see that by

73
00:03:20,750 --> 00:03:22,669
setting it to empty before using it.

74
00:03:22,669 --> 00:03:24,229
So we come back up to here, and on line

75
00:03:24,229 --> 00:03:28,939
3, I type people equals and left

76
00:03:28,939 --> 00:03:30,199
and right square brackets to create an empty

77
00:03:30,199 --> 00:03:34,629
list, and if we run this code again now,

78
00:03:34,629 --> 00:03:41,180
Sending email, as you can see there.

79
00:03:41,180 --> 00:03:42,769
And by the way, just ignore this warning over

80
00:03:42,769 --> 00:03:45,439
here: Redeclared 'people' defined above

81
00:03:45,439 --> 00:03:48,109
without usage. We're only

82
00:03:48,109 --> 00:03:50,209
changing people here to save modifying

83
00:03:50,209 --> 00:03:53,060
data.py, but again, you can see

84
00:03:53,060 --> 00:03:54,859
that wasn't what we wanted when we had an

85
00:03:54,859 --> 00:03:57,829
empty list here, defined. Our email

86
00:03:57,829 --> 00:03:59,509
program would attempt to send the email

87
00:03:59,509 --> 00:04:01,370
to an empty list of recipients and clearly,

88
00:04:01,370 --> 00:04:03,409
that's not what we'd want to do. So we

89
00:04:03,409 --> 00:04:05,329
either need a separate test or we can

90
00:04:05,329 --> 00:04:08,299
combine them, using the bool function. So

91
00:04:08,299 --> 00:04:10,400
if I modify line 5, and in fact, change

92
00:04:10,400 --> 00:04:14,060
that to if - and we'll have a test at the

93
00:04:14,060 --> 00:04:20,000
start first - if bool person and, and then

94
00:04:20,000 --> 00:04:21,769
all and as we had it before,

95
00:04:21,769 --> 00:04:24,080
so basically, adding that second test.

96
00:04:24,080 --> 00:04:28,020
That should've been people not person,

97
00:04:28,020 --> 00:04:32,550
and if we run this again now, "User must

98
00:04:32,550 --> 00:04:35,759
edit the list of recipients. So that works

99
00:04:35,759 --> 00:04:37,889
by adding a separate test, and just to

100
00:04:37,889 --> 00:04:39,750
confirm if that works now, we can

101
00:04:39,750 --> 00:04:42,509
actually comment out this line 3, when we

102
00:04:42,509 --> 00:04:43,919
created an empty list and run it again,

103
00:04:43,919 --> 00:04:47,220
and that still works with the actual

104
00:04:47,220 --> 00:04:49,680
valid data. So always test what are

105
00:04:49,680 --> 00:04:52,259
called edge cases. Things like values

106
00:04:52,259 --> 00:04:54,539
being 0 or negative, lists and

107
00:04:54,539 --> 00:04:56,550
dictionaries being empty, values that are

108
00:04:56,550 --> 00:04:58,289
far greater than you'd expect, things like

109
00:04:58,289 --> 00:05:00,900
that. Making sure that your code handles

110
00:05:00,900 --> 00:05:02,849
invalid data is the difference

111
00:05:02,849 --> 00:05:06,270
between an OK program and a great one. Alright,

112
00:05:06,270 --> 00:05:08,970
so for an example of using any, let's assume

113
00:05:08,970 --> 00:05:11,190
we're working on a shopping site that

114
00:05:11,190 --> 00:05:14,370
sells selections of plant seeds. The

115
00:05:14,370 --> 00:05:16,470
site's created some variety packs to

116
00:05:16,470 --> 00:05:19,590
make it easier for customers to choose. So

117
00:05:19,590 --> 00:05:20,789
instead of clicking on loads of

118
00:05:20,789 --> 00:05:22,860
individual seed packets, the site makes

119
00:05:22,860 --> 00:05:24,599
it easy for them to just specify some

120
00:05:24,599 --> 00:05:26,819
likes and dislikes, and offer some packs

121
00:05:26,819 --> 00:05:28,620
that fit what they want. So one of the

122
00:05:28,620 --> 00:05:30,960
options is grass. If you're a gardener,

123
00:05:30,960 --> 00:05:32,550
you'll know that there's some lovely

124
00:05:32,550 --> 00:05:34,830
grasses that can enhance a garden, but

125
00:05:34,830 --> 00:05:36,330
some people don't want to plant grasses.

126
00:05:36,330 --> 00:05:38,610
So what we can do here is, we can use any

127
00:05:38,610 --> 00:05:40,919
to check if the plants in a variety

128
00:05:40,919 --> 00:05:43,229
pack contain at least one plant that's a

129
00:05:43,229 --> 00:05:45,330
grass. So I'm going to add this code 

130
00:05:45,330 --> 00:05:47,870
after the last example.

131
00:05:47,870 --> 00:05:52,610
So on line 10, I can type if any

132
00:05:52,610 --> 00:05:57,150
parentheses square bracket plant dot

133
00:05:57,150 --> 00:06:02,599
plant underscore type is equal to, two equals

134
00:06:02,599 --> 00:06:06,180
double quotes grass and then a space

135
00:06:06,180 --> 00:06:10,919
there, then for plant in plants

136
00:06:10,919 --> 00:06:13,229
underscore list. Then we're going to have right

137
00:06:13,229 --> 00:06:15,389
square bracket right parenthesis and a

138
00:06:15,389 --> 00:06:18,479
colon. Then we can type print parentheses

139
00:06:18,479 --> 00:06:22,319
double quotes, this pack, This pack

140
00:06:22,319 --> 00:06:25,620
contains grass, closing double quote and

141
00:06:25,620 --> 00:06:28,500
parenthesis. Then introduce an else

142
00:06:28,500 --> 00:06:32,069
and in the else case we'll actually print no

143
00:06:32,069 --> 00:06:37,380
grasses in this pack and close it off. So

144
00:06:37,380 --> 00:06:41,600
when we run that now,

145
00:06:41,600 --> 00:06:44,580
you can see we've got this message down the

146
00:06:44,580 --> 00:06:45,600
bottom left hand corner, "This pack

147
00:06:45,600 --> 00:06:47,970
contains grass". So we can see that this

148
00:06:47,970 --> 00:06:50,250
pack does in fact contain at least

149
00:06:50,250 --> 00:06:52,979
one grass type. The comprehension in this

150
00:06:52,979 --> 00:06:54,870
example, creates a list containing true

151
00:06:54,870 --> 00:06:56,759
or false, depending on whether the

152
00:06:56,759 --> 00:06:59,130
condition's true for each plant in the

153
00:06:59,130 --> 00:07:02,160
list. Using a name tuple, I think and I'm

154
00:07:02,160 --> 00:07:03,449
sure you'll agree, makes the code

155
00:07:03,449 --> 00:07:05,789
more readable and saves us having to check

156
00:07:05,789 --> 00:07:08,130
the index position of plant underscore

157
00:07:08,130 --> 00:07:10,800
type in the tuple. Alright, so you can

158
00:07:10,800 --> 00:07:11,970
do the same thing with a

159
00:07:11,970 --> 00:07:14,039
dictionary instead and that's going to make a good

160
00:07:14,039 --> 00:07:15,690
challenge. Now just before I actually

161
00:07:15,690 --> 00:07:17,550
start the challenge though, the challenge

162
00:07:17,550 --> 00:07:18,990
is, it's going to involve a dictionary,

163
00:07:18,990 --> 00:07:21,150
as I mentioned, so I need

164
00:07:21,150 --> 00:07:22,650
to change the definitions up here. So I'm

165
00:07:22,650 --> 00:07:23,610
going to do that, I'm going to delete

166
00:07:23,610 --> 00:07:25,470
that basic underscore plants underscore

167
00:07:25,470 --> 00:07:27,330
list and instead what we're going to

168
00:07:27,330 --> 00:07:30,479
do is import as the third import, our

169
00:07:30,479 --> 00:07:31,979
plants underscore dict because we'll

170
00:07:31,979 --> 00:07:35,070
need that for the challenge. Alright so

171
00:07:35,070 --> 00:07:37,020
let's look at the challenge. The challenge is

172
00:07:37,020 --> 00:07:38,849
to use any and a comprehension, or

173
00:07:38,849 --> 00:07:41,250
generator expression if you prefer, to

174
00:07:41,250 --> 00:07:43,169
check the plants in plants underscore

175
00:07:43,169 --> 00:07:45,270
dict to see if there are any grasses in

176
00:07:45,270 --> 00:07:46,919
there. So you want to do that first and

177
00:07:46,919 --> 00:07:48,389
then run your code again after that,

178
00:07:48,389 --> 00:07:50,760
searching for cactus to test that it

179
00:07:50,760 --> 00:07:53,550
still works when there aren't any.

180
00:07:53,550 --> 00:07:54,930
Alright so that's the challenge. Pause the

181
00:07:54,930 --> 00:07:56,400
video. When you're ready to come back

182
00:07:56,400 --> 00:07:58,289
through my solution, come back and we'll

183
00:07:58,289 --> 00:08:01,510
go through that, so pause the video now.

184
00:08:01,510 --> 00:08:03,250
Alright so welcome back.

185
00:08:03,250 --> 00:08:05,230
Hopefully, you managed to get that solved. Let's go

186
00:08:05,230 --> 00:08:06,700
through and I'm going to show you the

187
00:08:06,700 --> 00:08:08,830
solution, and I'll add that just after

188
00:08:08,830 --> 00:08:10,360
the examples we've been working

189
00:08:10,360 --> 00:08:12,880
through in this video. I'm going to start on line

190
00:08:12,880 --> 00:08:17,880
15 by typing if any parentheses plant

191
00:08:17,880 --> 00:08:21,160
dot plant underscore type, that's equals

192
00:08:21,160 --> 00:08:25,120
to double quotes grass, capital G, for

193
00:08:25,120 --> 00:08:31,210
plant in plants underscore dict dot

194
00:08:31,210 --> 00:08:34,510
values then parentheses and a right

195
00:08:34,510 --> 00:08:36,539
parenthesis to close it off and a colon.

196
00:08:36,539 --> 00:08:39,690
So that's the case - we're going to print

197
00:08:39,690 --> 00:08:42,690
parentheses double quotes This dict

198
00:08:42,690 --> 00:08:47,740
contains grasses, else colon on the next

199
00:08:47,740 --> 00:08:49,410
line, print on the line after that,

200
00:08:49,410 --> 00:08:52,990
parentheses double quotes No grasses

201
00:08:52,990 --> 00:08:56,890
in the dict. So what I've done there, I've used

202
00:08:56,890 --> 00:08:58,770
a generator expression to save memory.

203
00:08:58,770 --> 00:09:00,640
Because we're dealing with a dictionary,

204
00:09:00,640 --> 00:09:02,980
the generator expression iterates over

205
00:09:02,980 --> 00:09:05,740
the dictionaries values view, as you can

206
00:09:05,740 --> 00:09:06,430
see over here

207
00:09:06,430 --> 00:09:08,950
on line 15, and checks the plant

208
00:09:08,950 --> 00:09:11,740
underscore type field of each value. So

209
00:09:11,740 --> 00:09:13,560
if we run that to make sure it works,

210
00:09:13,560 --> 00:09:15,670
and we can see that the dict contains

211
00:09:15,670 --> 00:09:18,070
grasses. Now you may have iterated over

212
00:09:18,070 --> 00:09:20,560
the keys instead, and that would also work

213
00:09:20,560 --> 00:09:22,090
fine - that's another way of doing it.

214
00:09:22,090 --> 00:09:23,770
So let's have a look at how to do

215
00:09:23,770 --> 00:09:25,990
that, and there we'll start, we'll

216
00:09:25,990 --> 00:09:28,930
start on line 20; if any plants

217
00:09:28,930 --> 00:09:32,650
underscore dict left square bracket key

218
00:09:32,650 --> 00:09:35,350
right square bracket dot plant

219
00:09:35,350 --> 00:09:39,210
underscore type, that's equal to Grass,

220
00:09:39,210 --> 00:09:44,500
and then for key in plants underscore

221
00:09:44,500 --> 00:09:46,060
dict. So that's the alternative way

222
00:09:46,060 --> 00:09:47,980
of doing it. Again iterating over the

223
00:09:47,980 --> 00:09:49,600
keys, and everything else will,

224
00:09:49,600 --> 00:09:51,130
effectively, be the same after that. We

225
00:09:51,130 --> 00:09:52,780
can just copy the same three lines there,

226
00:09:52,780 --> 00:10:00,700
and if we run that, we get the same

227
00:10:00,700 --> 00:10:04,450
output, as you can see there. So the code's

228
00:10:04,450 --> 00:10:06,430
a little bit less readable, but may

229
00:10:06,430 --> 00:10:08,500
perform better than iterating over the

230
00:10:08,500 --> 00:10:11,320
values view. If you, if you think that's

231
00:10:11,320 --> 00:10:12,670
likely to be an issue in your particular

232
00:10:12,670 --> 00:10:15,100
application, you can test the performance

233
00:10:15,100 --> 00:10:17,470
of both methods. We saw how to do that, of

234
00:10:17,470 --> 00:10:18,700
course, in an earlier video in this

235
00:10:18,700 --> 00:10:20,770
section of the course. Alright, so lastly,

236
00:10:20,770 --> 00:10:22,450
let's change the last generator

237
00:10:22,450 --> 00:10:24,730
expression to check for cactus instead,

238
00:10:24,730 --> 00:10:26,580
just to make sure that it's working

239
00:10:26,580 --> 00:10:29,710
and there is what we're looking for, and also

240
00:10:29,710 --> 00:10:31,480
when there isn't. So we can go ahead and

241
00:10:31,480 --> 00:10:40,210
just change Grass there to Cactus.

242
00:10:40,210 --> 00:10:49,870
Change the output, run the program, and you

243
00:10:49,870 --> 00:10:52,600
can see there's now cactii in the dict.

244
00:10:52,600 --> 00:10:54,310
Remember to test if there's an empty

245
00:10:54,310 --> 00:10:56,500
dictionary as well. It does work -

246
00:10:56,500 --> 00:10:58,420
any behaves as expected with an

247
00:10:58,420 --> 00:11:00,550
empty interable, but really you should

248
00:11:00,550 --> 00:11:02,950
still test it anyway. Alright, so that's

249
00:11:02,950 --> 00:11:05,620
the any and all functions. They're very

250
00:11:05,620 --> 00:11:07,350
easy to use and can be extremely

251
00:11:07,350 --> 00:11:09,340
useful. So I'm going to finish this section

252
00:11:09,340 --> 00:11:11,770
now with a quick look at lambdas. Now

253
00:11:11,770 --> 00:11:13,330
that's something else that Guido doesn't

254
00:11:13,330 --> 00:11:15,760
like but they can have their uses. So

255
00:11:15,760 --> 00:11:19,110
I'll see you in the next video.

