1
00:00:05,680 --> 00:00:07,200
Alright so I mentioned in the last video

2
00:00:07,200 --> 00:00:09,420
that we skimmed over a couple of things that I've done

3
00:00:09,420 --> 00:00:11,540
in the prior class, so let's go back and have a look

4
00:00:11,540 --> 00:00:13,640
at those in a bit more detail.

5
00:00:13,640 --> 00:00:16,610
So I'm gonna open the player.py file, so the first thing

6
00:00:16,610 --> 00:00:19,620
we did before adding the lives property was to hide

7
00:00:19,620 --> 00:00:24,450
the lives data attribute by prefixing it with an underscore.

8
00:00:24,450 --> 00:00:27,090
Now that's good practise so that the client code doesn't

9
00:00:27,090 --> 00:00:31,050
try to access the attribute directly by mistake.

10
00:00:31,050 --> 00:00:32,740
Now there's nothing to stop anyone from accessing

11
00:00:32,740 --> 00:00:35,590
the underscore lives attribute, this is Python and nothing's

12
00:00:35,590 --> 00:00:39,310
hidden, but you now have to do it deliberately.

13
00:00:39,310 --> 00:00:42,060
So getting back to the main programme, I can actually

14
00:00:42,060 --> 00:00:46,230
do something like tim._lives equals 9 print(tim)

15
00:00:52,870 --> 00:00:56,010
and then run it, and you can see that that does work,

16
00:00:56,010 --> 00:00:59,510
and obviously with the value of 9 there for lives.

17
00:00:59,510 --> 00:01:01,780
So as I've said a few times, Python doesn't enforce

18
00:01:01,780 --> 00:01:03,890
any idea of private variables.

19
00:01:03,890 --> 00:01:07,010
You're free to change anything you want, but if the creator

20
00:01:07,010 --> 00:01:10,360
of the class has hidden that data attribute by prefixing

21
00:01:10,360 --> 00:01:13,420
it's name with an underscore, then you may cause problems

22
00:01:13,420 --> 00:01:16,110
if you do go changing it.

23
00:01:16,110 --> 00:01:18,670
So I'm gonna delete line 19, now watch what happens

24
00:01:18,670 --> 00:01:20,270
when I retype it, you may have missed this the last time,

25
00:01:20,270 --> 00:01:24,430
so if I type in tim., intellij is actually dropping down

26
00:01:25,370 --> 00:01:28,230
the list of all the methods and attributes of the player

27
00:01:28,230 --> 00:01:30,890
class that we might wanna change.

28
00:01:30,890 --> 00:01:35,060
Now the list starts off as lives, level, name and score.

29
00:01:37,050 --> 00:01:39,190
After that, there are attributes and methods that

30
00:01:39,190 --> 00:01:42,120
our player class inherits from it's base class which

31
00:01:42,120 --> 00:01:43,170
is object.

32
00:01:43,170 --> 00:01:44,610
I'll come back to that a little bit later when we talk

33
00:01:44,610 --> 00:01:46,060
about inheritance.

34
00:01:46,060 --> 00:01:49,410
But notice though that the _lives doesn't appear anywhere

35
00:01:49,410 --> 00:01:53,050
in the list, so we can access it if we know it's there,

36
00:01:53,050 --> 00:01:56,930
but doing so accidentally is extremely unlikely.

37
00:01:56,930 --> 00:02:01,100
Alright so I'll finish typing the line, _lives equals 9,

38
00:02:02,640 --> 00:02:04,620
now there is another reason why we had to hide the lives

39
00:02:04,620 --> 00:02:08,000
attribute, now the result if you forget to do so can be

40
00:02:08,000 --> 00:02:11,260
extremely confusing if you're not aware of it.

41
00:02:11,260 --> 00:02:13,610
So I'm gonna pretend that I forgot, so come over here

42
00:02:13,610 --> 00:02:16,420
to the player class again.

43
00:02:16,420 --> 00:02:20,100
Alright we're gonna make the change here on line 5,

44
00:02:20,100 --> 00:02:23,300
so the easiest way is to get intelliJ to refactor it,

45
00:02:23,300 --> 00:02:27,380
so I'm gonna right-click here on the _lives on line 5,

46
00:02:27,380 --> 00:02:29,720
choose Refactor then Rename.

47
00:02:31,100 --> 00:02:32,710
And all we need to do is delete the underscore from the

48
00:02:32,710 --> 00:02:35,820
start of the line and click on Refactor, and that should

49
00:02:35,820 --> 00:02:37,970
change the code there, you can see the code was changed

50
00:02:37,970 --> 00:02:42,060
as I did that, we've got our self.lives equals 3 here,

51
00:02:42,060 --> 00:02:45,320
we've got self.lives here on line 14 and also here

52
00:02:45,320 --> 00:02:47,900
on line 17, self.lives as well.

53
00:02:48,880 --> 00:02:53,040
Alright so let's switch back to main and run this again.

54
00:02:55,530 --> 00:02:58,230
Now I'm sure that's probably not what you expected.

55
00:02:58,230 --> 00:03:00,490
Let's put an end of line at the end of the script

56
00:03:00,490 --> 00:03:02,940
so that it gets rid of that warning.

57
00:03:02,940 --> 00:03:04,960
So the problem that starts here, the reason why it's crashed

58
00:03:04,960 --> 00:03:08,710
test starts at line 3 of the player class.

59
00:03:08,710 --> 00:03:11,550
Where we set lives equal to 3.

60
00:03:11,550 --> 00:03:15,200
Now we've also got our property lives on line 19,

61
00:03:15,200 --> 00:03:19,660
so when Python sees set lives equals 3, it calls the setter

62
00:03:19,660 --> 00:03:23,250
with the value three, now inside the setter, here we're

63
00:03:23,250 --> 00:03:26,430
talking about the code here on line 12, the set of code

64
00:03:26,430 --> 00:03:31,100
which starts on line 13, the line self.lives equals lives

65
00:03:31,100 --> 00:03:34,400
is executed, so Python knows the value of lives that we

66
00:03:34,400 --> 00:03:37,540
pass to the setter is 3, so it calls the setter with

67
00:03:37,540 --> 00:03:41,090
the value 3, and inside the setter is the line self.lives

68
00:03:41,090 --> 00:03:44,910
equals lives, and you're probably getting the idea by now.

69
00:03:44,910 --> 00:03:47,180
Getting back to looking at the error down here,

70
00:03:47,180 --> 00:03:50,490
and we've got a recursion error, maximum recursion depth

71
00:03:50,490 --> 00:03:53,910
exceeded, and that's because the setter keeps calling itself

72
00:03:53,910 --> 00:03:57,580
recursively and there's no exit condition, so it just goes

73
00:03:57,580 --> 00:04:00,300
round and round until it overflows the stack and that's

74
00:04:00,300 --> 00:04:02,300
why we're getting this recursion error.

75
00:04:02,300 --> 00:04:04,980
So the important point about all this is that the data

76
00:04:04,980 --> 00:04:08,470
attribute mustn't have the same name as the property.

77
00:04:08,470 --> 00:04:11,030
Now we could have renamed the lives attribute to anything,

78
00:04:11,030 --> 00:04:13,520
as long as it wasn't the same name as we used for

79
00:04:13,520 --> 00:04:17,610
the property, getting back to here on line 19.

80
00:04:17,610 --> 00:04:20,420
So it makes sense just to put a print to just to prefix it

81
00:04:20,420 --> 00:04:24,260
with an underscore because that hides the attribute as well.

82
00:04:24,260 --> 00:04:27,520
Alright so let's go ahead now and rename the lives data

83
00:04:27,520 --> 00:04:30,710
attribute back to _lives again.

84
00:04:30,710 --> 00:04:33,770
Now we can obviously refactor again, but we can just use

85
00:04:33,770 --> 00:04:38,060
CTRL + Z on a PC, or Command + Z on a Mac to undo

86
00:04:38,060 --> 00:04:40,170
that renaming, so let's do that, making sure that we're

87
00:04:40,170 --> 00:04:44,330
in player.py, we'll do Command + Z, undo remaining variable

88
00:04:45,480 --> 00:04:46,810
_lives to lives?

89
00:04:48,560 --> 00:04:50,680
That puts the change back to what it was before we changed

90
00:04:50,680 --> 00:04:55,100
or renamed it, and if we go back to main.py, we should be

91
00:04:55,100 --> 00:04:57,350
able to run this again now.

92
00:04:58,770 --> 00:05:02,000
And you can see this time it's working as it was before.

93
00:05:02,000 --> 00:05:04,390
So watch out for that, Python really isn't happy if you

94
00:05:04,390 --> 00:05:07,240
give your property the same name as the data attribute

95
00:05:07,240 --> 00:05:08,650
backing it.

96
00:05:08,650 --> 00:05:11,990
Alright, so going back to player.py, the other thing

97
00:05:11,990 --> 00:05:14,230
that I did skim over was setting that property here

98
00:05:14,230 --> 00:05:16,230
on line 19 in player.py.

99
00:05:17,210 --> 00:05:19,520
And that's fairly straightforward, just the name

100
00:05:19,520 --> 00:05:22,530
of the property equals and a call to the properties

101
00:05:22,530 --> 00:05:26,450
class constructor to create the new property.

102
00:05:26,450 --> 00:05:29,380
Now I could hover the mouse over property and hold down

103
00:05:29,380 --> 00:05:32,940
the CTRL key or command on a Mac, you can see what

104
00:05:32,940 --> 00:05:35,180
the parameters are, but sometimes it's just easier

105
00:05:35,180 --> 00:05:39,570
to jump into the source by clicking on it which I'll do now.

106
00:05:39,570 --> 00:05:43,120
So the first parameter here is self, and that's because

107
00:05:43,120 --> 00:05:45,310
we're using this inside a class definition, so we can skip

108
00:05:45,310 --> 00:05:49,320
that, the next we specify up the three methods.

109
00:05:49,320 --> 00:05:51,680
And as they say in the doc string below the underscore

110
00:05:51,680 --> 00:05:55,110
underscore init method definition, if get is the name

111
00:05:55,110 --> 00:05:58,640
down here, function that's used for getting the attribute

112
00:05:58,640 --> 00:06:01,820
value, if set is the name of a function that will set

113
00:06:01,820 --> 00:06:05,290
the value and if del identifies a function that can

114
00:06:05,290 --> 00:06:08,300
be used to delete the attribute.

115
00:06:08,300 --> 00:06:11,240
And the final parameter, doc over here, that's a doc string

116
00:06:11,240 --> 00:06:14,130
you can use to provide a description of the property.

117
00:06:14,130 --> 00:06:17,640
Now we don't have all four parameters, so if I need to

118
00:06:17,640 --> 00:06:20,070
supply at the parameters to get and set

119
00:06:20,070 --> 00:06:21,820
our lives properties.

120
00:06:23,230 --> 00:06:25,500
You can see that on line 19.

121
00:06:25,500 --> 00:06:27,930
Now a delete a method doesn't really make sense when dealing

122
00:06:27,930 --> 00:06:30,790
with numbers, but could well be useful if the property

123
00:06:30,790 --> 00:06:33,550
is a list and you wanted a quick way to clear out

124
00:06:33,550 --> 00:06:36,240
all the items in the list.

125
00:06:36,240 --> 00:06:38,920
One thing to watch out for though, looking at line 19 again,

126
00:06:38,920 --> 00:06:42,460
is that you provide the names of the methods.

127
00:06:42,460 --> 00:06:44,740
Now if you place opening and closing parentheses after

128
00:06:44,740 --> 00:06:46,410
the names like this,

129
00:06:51,280 --> 00:06:53,550
then you're actually calling the method and the property's

130
00:06:53,550 --> 00:06:56,230
getter will be set to whatever the result of calling

131
00:06:56,230 --> 00:07:01,070
get_lives is, almost certainly not anything useful.

132
00:07:01,070 --> 00:07:03,080
I'll leave you to run the programme and see what happens

133
00:07:03,080 --> 00:07:05,640
if you do with the parentheses instead of just passing

134
00:07:05,640 --> 00:07:07,380
the method names.

135
00:07:07,380 --> 00:07:10,200
Now IntelliJ warns us that this is a strange argument,

136
00:07:10,200 --> 00:07:12,810
and that a callable is expected.

137
00:07:12,810 --> 00:07:15,370
Which is slightly ironic considering it tries to add

138
00:07:15,370 --> 00:07:18,910
the parentheses for us when we type the method names.

139
00:07:18,910 --> 00:07:21,890
And just to show you that, you probably saw this before

140
00:07:21,890 --> 00:07:26,070
but if I type _set_lives, it's automatically adding

141
00:07:26,070 --> 00:07:29,030
the parentheses, so it's sort of missing the point here,

142
00:07:29,030 --> 00:07:31,970
I will delete these two parentheses anyway.

143
00:07:31,970 --> 00:07:35,650
Now if you don't specify a method to use for the setter,

144
00:07:35,650 --> 00:07:38,410
then the property is gonna be read only which is sometimes

145
00:07:38,410 --> 00:07:42,590
useful, now if you specify setter but no getter,

146
00:07:42,590 --> 00:07:44,730
then you can change the value of the property but you can't

147
00:07:44,730 --> 00:07:47,330
display it, and that's really the less useful,

148
00:07:47,330 --> 00:07:49,850
possibly maybe for a password attribute.

149
00:07:49,850 --> 00:07:52,080
If you can think of a use for a write only property,

150
00:07:52,080 --> 00:07:54,150
then that's how you'd actually do it.

151
00:07:54,150 --> 00:07:56,870
The other thing I want to mention is that STI method

152
00:07:56,870 --> 00:07:59,100
and the strange replacement fields that I've used on line

153
00:07:59,100 --> 00:08:02,620
21 and 22, now I've been using replacement fields in

154
00:08:02,620 --> 00:08:07,530
the .format method a lot, but so far we've either used

155
00:08:07,530 --> 00:08:10,420
just opening and closing braces, or put a number inside them

156
00:08:10,420 --> 00:08:14,470
to indicate which of the values should go where.

157
00:08:14,470 --> 00:08:18,110
But when dealing with classes, you can also use this form.

158
00:08:18,110 --> 00:08:21,260
Now because the value supplied to format is an object,

159
00:08:21,260 --> 00:08:24,270
in this case self, the current instance of our player class,

160
00:08:24,270 --> 00:08:26,920
we only need to specify it once.

161
00:08:26,920 --> 00:08:29,400
We can then access it's data attributes and properties

162
00:08:29,400 --> 00:08:32,049
inside the replacement fields.

163
00:08:32,049 --> 00:08:34,870
So I think you'd agree that the way we've got it there

164
00:08:34,870 --> 00:08:37,809
is a lot more readable than another scenario which I'll

165
00:08:37,809 --> 00:08:40,559
just copy and paste this code in.

166
00:08:43,120 --> 00:08:44,530
Than that method there, and I'll just comment one of these

167
00:08:44,530 --> 00:08:46,620
out to keep things happy.

168
00:08:49,230 --> 00:08:52,580
So I think the first method, 0.name, 0.lives, 0.level,

169
00:08:52,580 --> 00:08:54,520
is a lot more readable than that second version which

170
00:08:54,520 --> 00:08:57,620
we've been using so far in the course.

171
00:08:57,620 --> 00:08:59,930
I'll just put it back to where it was.

172
00:08:59,930 --> 00:09:02,900
Alright so I think at this point, it's time for challenge.

173
00:09:02,900 --> 00:09:06,580
So here's the challenge, so the challenge is to modify

174
00:09:06,580 --> 00:09:10,790
the player class so that the player score is increased

175
00:09:10,790 --> 00:09:15,300
to 1000 every time their level increases by one.

176
00:09:15,300 --> 00:09:17,050
So if they jump up two levels, they'll get a bonus

177
00:09:17,050 --> 00:09:19,400
of 2000 added to their score.

178
00:09:19,400 --> 00:09:22,090
If the player drops back a level, they'll lose 1000

179
00:09:22,090 --> 00:09:24,850
for each level they drop back.

180
00:09:24,850 --> 00:09:27,160
They can't go below Level One, so your solution should

181
00:09:27,160 --> 00:09:29,230
prevent that from happening.

182
00:09:29,230 --> 00:09:31,590
So the aim of this challenge is to practise properties,

183
00:09:31,590 --> 00:09:34,730
so although it may make more sense to add methods

184
00:09:34,730 --> 00:09:37,240
to increase and decrease the level, please don't do it

185
00:09:37,240 --> 00:09:39,650
that way, use a property.

186
00:09:39,650 --> 00:09:42,080
Alright, so that's the challenge, see how you go with that,

187
00:09:42,080 --> 00:09:46,240
pause the video now and I'll see you when you get back.

188
00:09:48,270 --> 00:09:50,840
Alright, so because this video is about getters and setters,

189
00:09:50,840 --> 00:09:54,770
the solution I was looking for is to create a property

190
00:09:54,770 --> 00:09:57,660
for the level and then create the getter and a setter

191
00:09:57,660 --> 00:10:01,370
to change the score as the level changes.

192
00:10:01,370 --> 00:10:04,680
Now we're going to create a property called level,

193
00:10:04,680 --> 00:10:06,910
so the first thing is to hide our level attribute

194
00:10:06,910 --> 00:10:09,590
by renaming it to _level.

195
00:10:09,590 --> 00:10:14,360
So I'm gonna right click the level on line 6 and refactor it

196
00:10:14,360 --> 00:10:17,610
Rename, and we can rename it to _level.

197
00:10:20,230 --> 00:10:24,730
Next we need to create a getter and set our methods.

198
00:10:24,730 --> 00:10:26,720
So we're gonna add them under the getters and setters

199
00:10:26,720 --> 00:10:28,220
methods for lives.

200
00:10:29,780 --> 00:10:33,940
So def _get_level (self), and we've got a return,

201
00:10:38,880 --> 00:10:43,050
self._level, so the getter's obviously trivial there,

202
00:10:44,170 --> 00:10:48,170
we're just returning the value of the _level data attribute.

203
00:10:48,170 --> 00:10:50,690
Now the setter on the other hand has to check that the new

204
00:10:50,690 --> 00:10:55,120
level was greater than 0, then calculate the bonus that will

205
00:10:55,120 --> 00:10:59,900
be added to the score, so let's go ahead and write that.

206
00:10:59,900 --> 00:11:04,070
So def _set_level, adding self, and we also need to pass

207
00:11:06,360 --> 00:11:08,810
the parameter level.

208
00:11:08,810 --> 00:11:12,370
So I'm gonna start off by putting if level is greater than 0

209
00:11:12,370 --> 00:11:16,120
then put delta equals level take self._level.

210
00:11:20,880 --> 00:11:25,050
Then we wanna put self.score plus equals delta multiplied

211
00:11:26,890 --> 00:11:31,050
by 1000, by 1000, and self.level, self._level I should say,

212
00:11:34,620 --> 00:11:39,490
equals level, otherwise we're gonna put an else there,

213
00:11:39,490 --> 00:11:43,660
print("Level can't be less than 1").

214
00:11:49,050 --> 00:11:51,370
So if the new level is greater than 0, so 1 or more,

215
00:11:51,370 --> 00:11:55,510
we subtract the current level from it to find out how much

216
00:11:55,510 --> 00:11:57,090
it's increased by.

217
00:11:57,090 --> 00:11:59,830
Now if the level's gone down, delta will be negative

218
00:11:59,830 --> 00:12:02,390
and will be subtracting the bonus from the score,

219
00:12:02,390 --> 00:12:06,230
otherwise the bonus is added to the score.

220
00:12:06,230 --> 00:12:09,070
We're then updating the score and set the underscore level

221
00:12:09,070 --> 00:12:11,790
attribute to the new value.

222
00:12:11,790 --> 00:12:13,520
The last thing we have to do which I haven't done yet

223
00:12:13,520 --> 00:12:16,500
is to define the property and tell it to use our two methods

224
00:12:16,500 --> 00:12:19,030
as the getter and setter.

225
00:12:19,030 --> 00:12:21,670
So let's go ahead and do that.

226
00:12:21,670 --> 00:12:23,700
I'm gonna add this on line 31.

227
00:12:23,700 --> 00:12:27,860
So level is equal to property _get_level and _set_level,

228
00:12:32,200 --> 00:12:35,390
and it's almost like IntelliJ's got it in for us here,

229
00:12:35,390 --> 00:12:37,830
because it's always trying to add a call to a method

230
00:12:37,830 --> 00:12:41,300
when it offers it's suggestion as it did there.

231
00:12:41,300 --> 00:12:43,230
So that's why I mentioned it earlier and I'm mentioning

232
00:12:43,230 --> 00:12:45,890
it again now, we very definitely don't want the opening

233
00:12:45,890 --> 00:12:49,770
and closing parentheses after the method names.

234
00:12:49,770 --> 00:12:51,880
As that will result in the methods being called

235
00:12:51,880 --> 00:12:54,740
and their return value used for getter and setter,

236
00:12:54,740 --> 00:12:57,470
and it would be a very unusual getter that returned

237
00:12:57,470 --> 00:13:00,440
a method, and even if it did, it's not likely to be one

238
00:13:00,440 --> 00:13:03,450
that we'd want to use for our getter.

239
00:13:03,450 --> 00:13:05,920
Now a setter doesn't specify a value to return so it would

240
00:13:05,920 --> 00:13:09,770
return none, and the property setter would stay set to none.

241
00:13:09,770 --> 00:13:13,110
So in both cases not something that we want to happen.

242
00:13:13,110 --> 00:13:15,300
So just be very careful not to include the parentheses

243
00:13:15,300 --> 00:13:17,940
when setting up your properties.

244
00:13:17,940 --> 00:13:20,320
Alright so at this point now, I need to put a colon there

245
00:13:20,320 --> 00:13:21,230
on line 27.

246
00:13:22,870 --> 00:13:25,310
So at this point we can go back to our main.py and we

247
00:13:25,310 --> 00:13:27,620
can actually test this out.

248
00:13:27,620 --> 00:13:30,140
Alright so we're going to below the first definition

249
00:13:30,140 --> 00:13:32,140
of setting the lives to 9.

250
00:13:32,140 --> 00:13:35,990
Let's come down here and we're going to put tim.level

251
00:13:35,990 --> 00:13:40,160
equals 2, print tim, then on the next line, tim.level

252
00:13:42,760 --> 00:13:44,840
plus equals 5, print tim.

253
00:13:47,130 --> 00:13:49,870
Now hopefully we get a score ere, so that's just you see

254
00:13:49,870 --> 00:13:51,670
what it's doing.

255
00:13:51,670 --> 00:13:53,810
And we can see that we've got the results here,

256
00:13:53,810 --> 00:13:56,870
a score of 1000 then 6000 when we increase the level by 5

257
00:13:56,870 --> 00:13:59,050
more which is correct.

258
00:13:59,050 --> 00:14:01,170
Now decreasing the level should also work because

259
00:14:01,170 --> 00:14:05,910
I have delta, let's confirm that, tim.level equals 3,

260
00:14:05,910 --> 00:14:06,740
print tim.

261
00:14:10,880 --> 00:14:13,510
The level's going down by a total of 4, so our score

262
00:14:13,510 --> 00:14:17,000
is going down by 4000 in this case.

263
00:14:17,000 --> 00:14:19,720
So if your solution produces the same results,

264
00:14:19,720 --> 00:14:21,650
even if your code is different tomorrow,

265
00:14:21,650 --> 00:14:24,490
then congratulations, challenge passed.

266
00:14:24,490 --> 00:14:26,060
Alright, now there's one more thing that I need to talk

267
00:14:26,060 --> 00:14:28,930
about for these properties, but let's look at doing that

268
00:14:28,930 --> 00:14:30,430
in the next video.

