WEBVTT 1 00:00:02.490 --> 00:00:04.019 Alright so I mentioned in the last video 2 00:00:04.019 --> 00:00:06.236 that we skimmed over a couple of things that I've done 3 00:00:06.236 --> 00:00:08.350 in the prior class, so let's go back and have a look 4 00:00:08.350 --> 00:00:10.455 at those in a bit more detail. 5 00:00:10.455 --> 00:00:13.427 So I'm gonna open the player.py file, so the first thing 6 00:00:13.427 --> 00:00:16.435 we did before adding the lives property was to hide 7 00:00:16.435 --> 00:00:21.261 the lives data attribute by prefixing it with an underscore. 8 00:00:21.261 --> 00:00:23.904 Now that's good practise so that the client code doesn't 9 00:00:23.904 --> 00:00:27.861 try to access the attribute directly by mistake. 10 00:00:27.861 --> 00:00:29.556 Now there's nothing to stop anyone from accessing 11 00:00:29.556 --> 00:00:32.407 the underscore lives attribute, this is Python and nothing's 12 00:00:32.407 --> 00:00:36.124 hidden, but you now have to do it deliberately. 13 00:00:36.124 --> 00:00:38.879 So getting back to the main programme, I can actually 14 00:00:38.879 --> 00:00:43.046 do something like tim._lives equals 9 print(tim) 15 00:00:49.687 --> 00:00:52.825 and then run it, and you can see that that does work, 16 00:00:52.825 --> 00:00:56.325 and obviously with the value of 9 there for lives. 17 00:00:56.325 --> 00:00:58.599 So as I've said a few times, Python doesn't enforce 18 00:00:58.599 --> 00:01:00.700 any idea of private variables. 19 00:01:00.700 --> 00:01:03.828 You're free to change anything you want, but if the creator 20 00:01:03.828 --> 00:01:07.174 of the class has hidden that data attribute by prefixing 21 00:01:07.174 --> 00:01:10.230 it's name with an underscore, then you may cause problems 22 00:01:10.230 --> 00:01:12.926 if you do go changing it. 23 00:01:12.926 --> 00:01:15.480 So I'm gonna delete line 19, now watch what happens 24 00:01:15.480 --> 00:01:17.082 when I retype it, you may have missed this the last time, 25 00:01:17.082 --> 00:01:21.249 so if I type in tim., intellij is actually dropping down 26 00:01:22.182 --> 00:01:25.040 the list of all the methods and attributes of the player 27 00:01:25.040 --> 00:01:27.708 class that we might wanna change. 28 00:01:27.708 --> 00:01:31.875 Now the list starts off as lives, level, name and score. 29 00:01:33.866 --> 00:01:36.005 After that, there are attributes and methods that 30 00:01:36.005 --> 00:01:38.932 our player class inherits from it's base class which 31 00:01:38.932 --> 00:01:39.989 is object. 32 00:01:39.989 --> 00:01:41.426 I'll come back to that a little bit later when we talk 33 00:01:41.426 --> 00:01:42.872 about inheritance. 34 00:01:42.872 --> 00:01:46.223 But notice though that the _lives doesn't appear anywhere 35 00:01:46.223 --> 00:01:49.860 in the list, so we can access it if we know it's there, 36 00:01:49.860 --> 00:01:53.746 but doing so accidentally is extremely unlikely. 37 00:01:53.746 --> 00:01:57.913 Alright so I'll finish typing the line, _lives equals 9, 38 00:01:59.454 --> 00:02:01.438 now there is another reason why we had to hide the lives 39 00:02:01.438 --> 00:02:04.816 attribute, now the result if you forget to do so can be 40 00:02:04.816 --> 00:02:08.079 extremely confusing if you're not aware of it. 41 00:02:08.079 --> 00:02:10.425 So I'm gonna pretend that I forgot, so come over here 42 00:02:10.425 --> 00:02:13.235 to the player class again. 43 00:02:13.235 --> 00:02:16.919 Alright we're gonna make the change here on line 5, 44 00:02:16.919 --> 00:02:20.115 so the easiest way is to get intelliJ to refactor it, 45 00:02:20.115 --> 00:02:24.199 so I'm gonna right-click here on the _lives on line 5, 46 00:02:24.199 --> 00:02:26.532 choose Refactor then Rename. 47 00:02:27.910 --> 00:02:29.524 And all we need to do is delete the underscore from the 48 00:02:29.524 --> 00:02:32.638 start of the line and click on Refactor, and that should 49 00:02:32.638 --> 00:02:34.783 change the code there, you can see the code was changed 50 00:02:34.783 --> 00:02:38.876 as I did that, we've got our self.lives equals 3 here, 51 00:02:38.876 --> 00:02:42.133 we've got self.lives here on line 14 and also here 52 00:02:42.133 --> 00:02:44.716 on line 17, self.lives as well. 53 00:02:45.691 --> 00:02:49.858 Alright so let's switch back to main and run this again. 54 00:02:52.342 --> 00:02:55.040 Now I'm sure that's probably not what you expected. 55 00:02:55.040 --> 00:02:57.305 Let's put an end of line at the end of the script 56 00:02:57.305 --> 00:02:59.750 so that it gets rid of that warning. 57 00:02:59.750 --> 00:03:01.771 So the problem that starts here, the reason why it's crashed 58 00:03:01.771 --> 00:03:05.523 test starts at line 3 of the player class. 59 00:03:05.523 --> 00:03:08.364 Where we set lives equal to 3. 60 00:03:08.364 --> 00:03:12.014 Now we've also got our property lives on line 19, 61 00:03:12.014 --> 00:03:16.474 so when Python sees set lives equals 3, it calls the setter 62 00:03:16.474 --> 00:03:20.062 with the value three, now inside the setter, here we're 63 00:03:20.062 --> 00:03:23.240 talking about the code here on line 12, the set of code 64 00:03:23.240 --> 00:03:27.915 which starts on line 13, the line self.lives equals lives 65 00:03:27.915 --> 00:03:31.218 is executed, so Python knows the value of lives that we 66 00:03:31.218 --> 00:03:34.351 pass to the setter is 3, so it calls the setter with 67 00:03:34.351 --> 00:03:37.908 the value 3, and inside the setter is the line self.lives 68 00:03:37.908 --> 00:03:41.722 equals lives, and you're probably getting the idea by now. 69 00:03:41.722 --> 00:03:43.993 Getting back to looking at the error down here, 70 00:03:43.993 --> 00:03:47.305 and we've got a recursion error, maximum recursion depth 71 00:03:47.305 --> 00:03:50.721 exceeded, and that's because the setter keeps calling itself 72 00:03:50.721 --> 00:03:54.397 recursively and there's no exit condition, so it just goes 73 00:03:54.397 --> 00:03:57.116 round and round until it overflows the stack and that's 74 00:03:57.116 --> 00:03:59.116 why we're getting this recursion error. 75 00:03:59.116 --> 00:04:01.790 So the important point about all this is that the data 76 00:04:01.790 --> 00:04:05.283 attribute mustn't have the same name as the property. 77 00:04:05.283 --> 00:04:07.847 Now we could have renamed the lives attribute to anything, 78 00:04:07.847 --> 00:04:10.332 as long as it wasn't the same name as we used for 79 00:04:10.332 --> 00:04:14.429 the property, getting back to here on line 19. 80 00:04:14.429 --> 00:04:17.238 So it makes sense just to put a print to just to prefix it 81 00:04:17.238 --> 00:04:21.076 with an underscore because that hides the attribute as well. 82 00:04:21.076 --> 00:04:24.338 Alright so let's go ahead now and rename the lives data 83 00:04:24.338 --> 00:04:27.529 attribute back to _lives again. 84 00:04:27.529 --> 00:04:30.587 Now we can obviously refactor again, but we can just use 85 00:04:30.587 --> 00:04:34.876 CTRL + Z on a PC, or Command + Z on a Mac to undo 86 00:04:34.876 --> 00:04:36.980 that renaming, so let's do that, making sure that we're 87 00:04:36.980 --> 00:04:41.147 in player.py, we'll do Command + Z, undo remaining variable 88 00:04:42.293 --> 00:04:43.626 _lives to lives? 89 00:04:45.375 --> 00:04:47.497 That puts the change back to what it was before we changed 90 00:04:47.497 --> 00:04:51.912 or renamed it, and if we go back to main.py, we should be 91 00:04:51.912 --> 00:04:54.162 able to run this again now. 92 00:04:55.586 --> 00:04:58.815 And you can see this time it's working as it was before. 93 00:04:58.815 --> 00:05:01.203 So watch out for that, Python really isn't happy if you 94 00:05:01.203 --> 00:05:04.058 give your property the same name as the data attribute 95 00:05:04.058 --> 00:05:05.460 backing it. 96 00:05:05.460 --> 00:05:08.809 Alright, so going back to player.py, the other thing 97 00:05:08.809 --> 00:05:11.042 that I did skim over was setting that property here 98 00:05:11.042 --> 00:05:13.042 on line 19 in player.py. 99 00:05:14.026 --> 00:05:16.336 And that's fairly straightforward, just the name 100 00:05:16.336 --> 00:05:19.341 of the property equals and a call to the properties 101 00:05:19.341 --> 00:05:23.267 class constructor to create the new property. 102 00:05:23.267 --> 00:05:26.196 Now I could hover the mouse over property and hold down 103 00:05:26.196 --> 00:05:29.752 the CTRL key or command on a Mac, you can see what 104 00:05:29.752 --> 00:05:31.998 the parameters are, but sometimes it's just easier 105 00:05:31.998 --> 00:05:36.382 to jump into the source by clicking on it which I'll do now. 106 00:05:36.382 --> 00:05:39.931 So the first parameter here is self, and that's because 107 00:05:39.931 --> 00:05:42.128 we're using this inside a class definition, so we can skip 108 00:05:42.128 --> 00:05:46.138 that, the next we specify up the three methods. 109 00:05:46.138 --> 00:05:48.494 And as they say in the doc string below the underscore 110 00:05:48.494 --> 00:05:51.922 underscore init method definition, if get is the name 111 00:05:51.922 --> 00:05:55.454 down here, function that's used for getting the attribute 112 00:05:55.454 --> 00:05:58.630 value, if set is the name of a function that will set 113 00:05:58.630 --> 00:06:02.107 the value and if del identifies a function that can 114 00:06:02.107 --> 00:06:05.116 be used to delete the attribute. 115 00:06:05.116 --> 00:06:08.050 And the final parameter, doc over here, that's a doc string 116 00:06:08.050 --> 00:06:10.948 you can use to provide a description of the property. 117 00:06:10.948 --> 00:06:14.454 Now we don't have all four parameters, so if I need to 118 00:06:14.454 --> 00:06:16.889 supply at the parameters to get and set 119 00:06:16.889 --> 00:06:18.639 our lives properties. 120 00:06:20.042 --> 00:06:22.315 You can see that on line 19. 121 00:06:22.315 --> 00:06:24.749 Now a delete a method doesn't really make sense when dealing 122 00:06:24.749 --> 00:06:27.603 with numbers, but could well be useful if the property 123 00:06:27.603 --> 00:06:30.368 is a list and you wanted a quick way to clear out 124 00:06:30.368 --> 00:06:33.054 all the items in the list. 125 00:06:33.054 --> 00:06:35.735 One thing to watch out for though, looking at line 19 again, 126 00:06:35.735 --> 00:06:39.274 is that you provide the names of the methods. 127 00:06:39.274 --> 00:06:41.553 Now if you place opening and closing parentheses after 128 00:06:41.553 --> 00:06:43.220 the names like this, 129 00:06:48.095 --> 00:06:50.369 then you're actually calling the method and the property's 130 00:06:50.369 --> 00:06:53.048 getter will be set to whatever the result of calling 131 00:06:53.048 --> 00:06:57.882 get_lives is, almost certainly not anything useful. 132 00:06:57.882 --> 00:06:59.898 I'll leave you to run the programme and see what happens 133 00:06:59.898 --> 00:07:02.453 if you do with the parentheses instead of just passing 134 00:07:02.453 --> 00:07:04.192 the method names. 135 00:07:04.192 --> 00:07:07.010 Now IntelliJ warns us that this is a strange argument, 136 00:07:07.010 --> 00:07:09.623 and that a callable is expected. 137 00:07:09.623 --> 00:07:12.184 Which is slightly ironic considering it tries to add 138 00:07:12.184 --> 00:07:15.726 the parentheses for us when we type the method names. 139 00:07:15.726 --> 00:07:18.700 And just to show you that, you probably saw this before 140 00:07:18.700 --> 00:07:22.885 but if I type _set_lives, it's automatically adding 141 00:07:22.885 --> 00:07:25.848 the parentheses, so it's sort of missing the point here, 142 00:07:25.848 --> 00:07:28.789 I will delete these two parentheses anyway. 143 00:07:28.789 --> 00:07:32.462 Now if you don't specify a method to use for the setter, 144 00:07:32.462 --> 00:07:35.227 then the property is gonna be read only which is sometimes 145 00:07:35.227 --> 00:07:39.405 useful, now if you specify setter but no getter, 146 00:07:39.405 --> 00:07:41.540 then you can change the value of the property but you can't 147 00:07:41.540 --> 00:07:44.143 display it, and that's really the less useful, 148 00:07:44.143 --> 00:07:46.668 possibly maybe for a password attribute. 149 00:07:46.668 --> 00:07:48.898 If you can think of a use for a write only property, 150 00:07:48.898 --> 00:07:50.963 then that's how you'd actually do it. 151 00:07:50.963 --> 00:07:53.681 The other thing I want to mention is that STI method 152 00:07:53.681 --> 00:07:55.914 and the strange replacement fields that I've used on line 153 00:07:55.914 --> 00:07:59.439 21 and 22, now I've been using replacement fields in 154 00:07:59.439 --> 00:08:04.343 the .format method a lot, but so far we've either used 155 00:08:04.343 --> 00:08:07.234 just opening and closing braces, or put a number inside them 156 00:08:07.234 --> 00:08:11.286 to indicate which of the values should go where. 157 00:08:11.286 --> 00:08:14.922 But when dealing with classes, you can also use this form. 158 00:08:14.922 --> 00:08:18.071 Now because the value supplied to format is an object, 159 00:08:18.071 --> 00:08:21.084 in this case self, the current instance of our player class, 160 00:08:21.084 --> 00:08:23.734 we only need to specify it once. 161 00:08:23.734 --> 00:08:26.219 We can then access it's data attributes and properties 162 00:08:26.219 --> 00:08:28.866 inside the replacement fields. 163 00:08:28.866 --> 00:08:31.689 So I think you'd agree that the way we've got it there 164 00:08:31.689 --> 00:08:34.620 is a lot more readable than another scenario which I'll 165 00:08:34.620 --> 00:08:37.370 just copy and paste this code in. 166 00:08:39.939 --> 00:08:41.348 Than that method there, and I'll just comment one of these 167 00:08:41.348 --> 00:08:43.431 out to keep things happy. 168 00:08:46.045 --> 00:08:49.393 So I think the first method, 0.name, 0.lives, 0.level, 169 00:08:49.393 --> 00:08:51.335 is a lot more readable than that second version which 170 00:08:51.335 --> 00:08:54.431 we've been using so far in the course. 171 00:08:54.431 --> 00:08:56.744 I'll just put it back to where it was. 172 00:08:56.744 --> 00:08:59.717 Alright so I think at this point, it's time for challenge. 173 00:08:59.717 --> 00:09:03.393 So here's the challenge, so the challenge is to modify 174 00:09:03.393 --> 00:09:07.608 the player class so that the player score is increased 175 00:09:07.608 --> 00:09:12.118 to 1000 every time their level increases by one. 176 00:09:12.118 --> 00:09:13.862 So if they jump up two levels, they'll get a bonus 177 00:09:13.862 --> 00:09:16.218 of 2000 added to their score. 178 00:09:16.218 --> 00:09:18.901 If the player drops back a level, they'll lose 1000 179 00:09:18.901 --> 00:09:21.669 for each level they drop back. 180 00:09:21.669 --> 00:09:23.979 They can't go below Level One, so your solution should 181 00:09:23.979 --> 00:09:26.045 prevent that from happening. 182 00:09:26.045 --> 00:09:28.406 So the aim of this challenge is to practise properties, 183 00:09:28.406 --> 00:09:31.547 so although it may make more sense to add methods 184 00:09:31.547 --> 00:09:34.057 to increase and decrease the level, please don't do it 185 00:09:34.057 --> 00:09:36.460 that way, use a property. 186 00:09:36.460 --> 00:09:38.892 Alright, so that's the challenge, see how you go with that, 187 00:09:38.892 --> 00:09:43.059 pause the video now and I'll see you when you get back. 188 00:09:45.086 --> 00:09:47.651 Alright, so because this video is about getters and setters, 189 00:09:47.651 --> 00:09:51.580 the solution I was looking for is to create a property 190 00:09:51.580 --> 00:09:54.475 for the level and then create the getter and a setter 191 00:09:54.475 --> 00:09:58.181 to change the score as the level changes. 192 00:09:58.181 --> 00:10:01.496 Now we're going to create a property called level, 193 00:10:01.496 --> 00:10:03.725 so the first thing is to hide our level attribute 194 00:10:03.725 --> 00:10:06.401 by renaming it to _level. 195 00:10:06.401 --> 00:10:11.173 So I'm gonna right click the level on line 6 and refactor it 196 00:10:11.173 --> 00:10:14.423 Rename, and we can rename it to _level. 197 00:10:17.044 --> 00:10:21.543 Next we need to create a getter and set our methods. 198 00:10:21.543 --> 00:10:23.532 So we're gonna add them under the getters and setters 199 00:10:23.532 --> 00:10:25.032 methods for lives. 200 00:10:26.592 --> 00:10:30.759 So def _get_level (self), and we've got a return, 201 00:10:35.693 --> 00:10:39.860 self._level, so the getter's obviously trivial there, 202 00:10:40.983 --> 00:10:44.983 we're just returning the value of the _level data attribute. 203 00:10:44.983 --> 00:10:47.509 Now the setter on the other hand has to check that the new 204 00:10:47.509 --> 00:10:51.938 level was greater than 0, then calculate the bonus that will 205 00:10:51.938 --> 00:10:56.714 be added to the score, so let's go ahead and write that. 206 00:10:56.714 --> 00:11:00.881 So def _set_level, adding self, and we also need to pass 207 00:11:03.170 --> 00:11:05.626 the parameter level. 208 00:11:05.626 --> 00:11:09.188 So I'm gonna start off by putting if level is greater than 0 209 00:11:09.188 --> 00:11:12.938 then put delta equals level take self._level. 210 00:11:17.699 --> 00:11:21.866 Then we wanna put self.score plus equals delta multiplied 211 00:11:23.702 --> 00:11:27.869 by 1000, by 1000, and self.level, self._level I should say, 212 00:11:31.430 --> 00:11:36.309 equals level, otherwise we're gonna put an else there, 213 00:11:36.309 --> 00:11:40.476 print("Level can't be less than 1"). 214 00:11:45.868 --> 00:11:48.188 So if the new level is greater than 0, so 1 or more, 215 00:11:48.188 --> 00:11:52.327 we subtract the current level from it to find out how much 216 00:11:52.327 --> 00:11:53.908 it's increased by. 217 00:11:53.908 --> 00:11:56.641 Now if the level's gone down, delta will be negative 218 00:11:56.641 --> 00:11:59.200 and will be subtracting the bonus from the score, 219 00:11:59.200 --> 00:12:03.044 otherwise the bonus is added to the score. 220 00:12:03.044 --> 00:12:05.889 We're then updating the score and set the underscore level 221 00:12:05.889 --> 00:12:08.603 attribute to the new value. 222 00:12:08.603 --> 00:12:10.339 The last thing we have to do which I haven't done yet 223 00:12:10.339 --> 00:12:13.316 is to define the property and tell it to use our two methods 224 00:12:13.316 --> 00:12:15.847 as the getter and setter. 225 00:12:15.847 --> 00:12:18.486 So let's go ahead and do that. 226 00:12:18.486 --> 00:12:20.511 I'm gonna add this on line 31. 227 00:12:20.511 --> 00:12:24.678 So level is equal to property _get_level and _set_level, 228 00:12:29.017 --> 00:12:32.209 and it's almost like IntelliJ's got it in for us here, 229 00:12:32.209 --> 00:12:34.647 because it's always trying to add a call to a method 230 00:12:34.647 --> 00:12:38.117 when it offers it's suggestion as it did there. 231 00:12:38.117 --> 00:12:40.044 So that's why I mentioned it earlier and I'm mentioning 232 00:12:40.044 --> 00:12:42.702 it again now, we very definitely don't want the opening 233 00:12:42.702 --> 00:12:46.582 and closing parentheses after the method names. 234 00:12:46.582 --> 00:12:48.694 As that will result in the methods being called 235 00:12:48.694 --> 00:12:51.553 and their return value used for getter and setter, 236 00:12:51.553 --> 00:12:54.282 and it would be a very unusual getter that returned 237 00:12:54.282 --> 00:12:57.256 a method, and even if it did, it's not likely to be one 238 00:12:57.256 --> 00:13:00.261 that we'd want to use for our getter. 239 00:13:00.261 --> 00:13:02.739 Now a setter doesn't specify a value to return so it would 240 00:13:02.739 --> 00:13:06.587 return none, and the property setter would stay set to none. 241 00:13:06.587 --> 00:13:09.925 So in both cases not something that we want to happen. 242 00:13:09.925 --> 00:13:12.118 So just be very careful not to include the parentheses 243 00:13:12.118 --> 00:13:14.755 when setting up your properties. 244 00:13:14.755 --> 00:13:17.130 Alright so at this point now, I need to put a colon there 245 00:13:17.130 --> 00:13:18.047 on line 27. 246 00:13:19.688 --> 00:13:22.129 So at this point we can go back to our main.py and we 247 00:13:22.129 --> 00:13:24.437 can actually test this out. 248 00:13:24.437 --> 00:13:26.955 Alright so we're going to below the first definition 249 00:13:26.955 --> 00:13:28.958 of setting the lives to 9. 250 00:13:28.958 --> 00:13:32.803 Let's come down here and we're going to put tim.level 251 00:13:32.803 --> 00:13:36.970 equals 2, print tim, then on the next line, tim.level 252 00:13:39.575 --> 00:13:41.658 plus equals 5, print tim. 253 00:13:43.944 --> 00:13:46.683 Now hopefully we get a score ere, so that's just you see 254 00:13:46.683 --> 00:13:48.485 what it's doing. 255 00:13:48.485 --> 00:13:50.629 And we can see that we've got the results here, 256 00:13:50.629 --> 00:13:53.681 a score of 1000 then 6000 when we increase the level by 5 257 00:13:53.681 --> 00:13:55.865 more which is correct. 258 00:13:55.865 --> 00:13:57.980 Now decreasing the level should also work because 259 00:13:57.980 --> 00:14:02.724 I have delta, let's confirm that, tim.level equals 3, 260 00:14:02.724 --> 00:14:03.557 print tim. 261 00:14:07.690 --> 00:14:10.321 The level's going down by a total of 4, so our score 262 00:14:10.321 --> 00:14:13.816 is going down by 4000 in this case. 263 00:14:13.816 --> 00:14:16.534 So if your solution produces the same results, 264 00:14:16.534 --> 00:14:18.462 even if your code is different tomorrow, 265 00:14:18.462 --> 00:14:21.309 then congratulations, challenge passed. 266 00:14:21.309 --> 00:14:22.873 Alright, now there's one more thing that I need to talk 267 00:14:22.873 --> 00:14:25.748 about for these properties, but let's look at doing that 268 00:14:25.748 --> 00:14:27.248 in the next video.