1 01:00:00,000 --> 01:00:03,458 [CLASSICAL MUSIC] 2 01:00:03,458 --> 01:00:23,875 3 01:00:23,875 --> 01:00:26,583 DAVID J. MALAN: All right, this is CS50's Introduction 4 01:00:26,583 --> 01:00:27,916 to Programming with Python. 5 01:00:27,916 --> 01:00:31,541 My name is David Malan, and this is our week on object-oriented programming, 6 01:00:31,541 --> 01:00:32,791 or OOP. 7 01:00:32,791 --> 01:00:34,750 It turns out that, in the world of programming, 8 01:00:34,750 --> 01:00:37,583 there's different paradigms of programming languages. 9 01:00:37,583 --> 01:00:40,208 There's different ways of solving problems with code, 10 01:00:40,208 --> 01:00:42,000 and it's a little hard to see this at first 11 01:00:42,000 --> 01:00:43,916 if you've only learned one language. 12 01:00:43,916 --> 01:00:47,416 But over time, if and when you learn other languages besides Python, 13 01:00:47,416 --> 01:00:50,083 you'll start to notice certain patterns and certain capabilities 14 01:00:50,083 --> 01:00:52,000 of some languages but not another. 15 01:00:52,000 --> 01:00:54,791 Thus far, within the world of Python, you and I 16 01:00:54,791 --> 01:00:58,541 have largely been writing code that's procedural in nature, whereby we're 17 01:00:58,541 --> 01:01:00,708 writing procedures; we're writing functions; 18 01:01:00,708 --> 01:01:03,166 and we're doing things top to bottom. 19 01:01:03,166 --> 01:01:05,458 Everything is step by step by step, as you would 20 01:01:05,458 --> 01:01:07,208 expect in general from an algorithm. 21 01:01:07,208 --> 01:01:09,250 But along the way, we've actually dabbled 22 01:01:09,250 --> 01:01:14,083 in another paradigm known as functional programming with Python whereby we've 23 01:01:14,083 --> 01:01:15,875 been able to pass functions around. 24 01:01:15,875 --> 01:01:18,666 We even had an anonymous function some weeks ago. 25 01:01:18,666 --> 01:01:22,500 And that's evidence of features of a functional programming language, 26 01:01:22,500 --> 01:01:24,708 even though we've just scratched the surface thereof. 27 01:01:24,708 --> 01:01:28,083 Today we focus on another paradigm, and this one in more detail-- 28 01:01:28,083 --> 01:01:30,166 namely object-oriented programming. 29 01:01:30,166 --> 01:01:32,958 And now, while some of you might have prior programming experience 30 01:01:32,958 --> 01:01:36,500 and have learned languages like Java, which are, by design, fundamentally 31 01:01:36,500 --> 01:01:40,291 object-oriented, Python indeed allows you a bit of flexibility 32 01:01:40,291 --> 01:01:43,250 when it comes to how you solve problems with code. 33 01:01:43,250 --> 01:01:46,458 But it turns out object-oriented programming 34 01:01:46,458 --> 01:01:49,000 is a pretty compelling solution to problems 35 01:01:49,000 --> 01:01:52,666 that you invariably encounter as your programs get longer, larger, 36 01:01:52,666 --> 01:01:54,375 and more complicated. 37 01:01:54,375 --> 01:01:56,333 So indeed, OOP, for our purposes, is going 38 01:01:56,333 --> 01:02:00,791 to be a solution to a problem that builds on so many of the lessons past. 39 01:02:00,791 --> 01:02:02,458 So let's go ahead and do this. 40 01:02:02,458 --> 01:02:05,833 Let's start by writing a program very procedurally 41 01:02:05,833 --> 01:02:07,625 by opening up VS Code here. 42 01:02:07,625 --> 01:02:10,875 I'm going to go ahead and create a program called student.py. 43 01:02:10,875 --> 01:02:14,083 And in this program, I want to do something relatively simple 44 01:02:14,083 --> 01:02:18,375 initially, as we might have done some weeks ago now, where I just 45 01:02:18,375 --> 01:02:22,166 ask a user for their name and, maybe in the context of the Harry Potter 46 01:02:22,166 --> 01:02:26,291 universe, their house, and just print out where that student is from. 47 01:02:26,291 --> 01:02:30,166 And let's gradually enhance this program by adding more and more features to it 48 01:02:30,166 --> 01:02:33,875 and see if we don't stumble upon problems that, up until now, 49 01:02:33,875 --> 01:02:36,958 we might not have had very elegant, well-designed solutions to. 50 01:02:36,958 --> 01:02:40,250 But if we introduce, explicitly, object-oriented programming 51 01:02:40,250 --> 01:02:43,250 as a programming technique, I bet we can clean up our code 52 01:02:43,250 --> 01:02:47,208 and set the stage for writing even more sophisticated programs, longer programs 53 01:02:47,208 --> 01:02:48,083 down the line. 54 01:02:48,083 --> 01:02:51,791 So in student.py, let me go ahead and do a name variable, 55 01:02:51,791 --> 01:02:54,250 setting it equal to the return value of input, 56 01:02:54,250 --> 01:02:57,041 and just prompt the user for their name like this. 57 01:02:57,041 --> 01:02:59,791 And then let me go ahead and do the same for a house variable 58 01:02:59,791 --> 01:03:03,041 and prompt the user for their house, using input like this. 59 01:03:03,041 --> 01:03:05,208 And let's do something super simple now. 60 01:03:05,208 --> 01:03:07,875 Let's just go ahead and print out an f string 61 01:03:07,875 --> 01:03:11,291 that says something like name from house, 62 01:03:11,291 --> 01:03:13,958 just so that I can confirm that the contents of these variables 63 01:03:13,958 --> 01:03:15,000 are indeed as I expect. 64 01:03:15,000 --> 01:03:17,083 I'm not going to do any error checking or trimming 65 01:03:17,083 --> 01:03:18,333 or anything like that for now. 66 01:03:18,333 --> 01:03:21,416 I'm really just going to spit back out whatever the user just typed in. 67 01:03:21,416 --> 01:03:24,375 All right, let me go ahead and run Python of student.py. 68 01:03:24,375 --> 01:03:29,083 Let's use our go-to, like Harry, as in Harry Potter, from Gryffindor. 69 01:03:29,083 --> 01:03:32,958 And when I hit Enter, now let's see if I see that Harry from Gryffindor 70 01:03:32,958 --> 01:03:34,041 is indeed the case. 71 01:03:34,041 --> 01:03:37,375 All right, so I think we have a working program at this point, but let's 72 01:03:37,375 --> 01:03:40,541 now introduce some of those lessons learned way back from week zero 73 01:03:40,541 --> 01:03:42,583 where we started writing our own functions, 74 01:03:42,583 --> 01:03:46,250 not necessarily because it solves the problem more correctly-- 75 01:03:46,250 --> 01:03:47,916 I daresay this is correct as is. 76 01:03:47,916 --> 01:03:52,041 But it begins to give us building blocks that we can extend so 77 01:03:52,041 --> 01:03:54,125 as to solve more complicated programs. 78 01:03:54,125 --> 01:03:58,375 So let me go back up to student.py, and let's go ahead now and do this. 79 01:03:58,375 --> 01:04:01,875 Let's put the entire logic I just wrote inside of our typical method 80 01:04:01,875 --> 01:04:05,458 called main, and let me indent those three lines so that at least they're 81 01:04:05,458 --> 01:04:07,333 now combined into one main method. 82 01:04:07,333 --> 01:04:12,208 But instead of using input on line 2 an input on line 3, 83 01:04:12,208 --> 01:04:14,083 don't we go ahead and assume, for the moment, 84 01:04:14,083 --> 01:04:17,000 that we've got some function called get_name in the world, 85 01:04:17,000 --> 01:04:19,958 and let's go ahead and assume we've got another function like get_house 86 01:04:19,958 --> 01:04:21,708 in the world that don't take parameters. 87 01:04:21,708 --> 01:04:24,166 But their purpose in life is, by their name, 88 01:04:24,166 --> 01:04:28,458 going to be to get the user's name and to get their users house, respectively. 89 01:04:28,458 --> 01:04:32,083 And then I'm going to print out the exact same f string as before. 90 01:04:32,083 --> 01:04:34,333 I, of course, need to implement these functions now. 91 01:04:34,333 --> 01:04:38,291 So let me go lower in my file and define a function called get_name. 92 01:04:38,291 --> 01:04:41,375 Nothing in these parentheses because it's not going to take a parameter. 93 01:04:41,375 --> 01:04:44,083 And I'm going to go ahead and do something like name equals input 94 01:04:44,083 --> 01:04:48,750 ("Name"), just like before, and then I'm going to go ahead and return name. 95 01:04:48,750 --> 01:04:52,000 So it's a super simple function, but it's an abstraction. 96 01:04:52,000 --> 01:04:55,458 I now have a function called get_name whose implementation details 97 01:04:55,458 --> 01:04:56,916 I don't have to care about anymore. 98 01:04:56,916 --> 01:04:58,458 I just know that the function exists. 99 01:04:58,458 --> 01:05:00,250 And I can tighten this up, in fact, . 100 01:05:00,250 --> 01:05:02,875 Don't really need a name variable on line 8 101 01:05:02,875 --> 01:05:05,875 if I'm immediately going to return that same name variable on line 9. 102 01:05:05,875 --> 01:05:07,833 So let me just tighten this up a little bit 103 01:05:07,833 --> 01:05:09,791 even though it doesn't change the functionality 104 01:05:09,791 --> 01:05:15,750 and just immediately return the return value of the inputs function call here. 105 01:05:15,750 --> 01:05:18,916 Let's do something very similar now for get_house, 106 01:05:18,916 --> 01:05:20,750 which will similarly take no arguments. 107 01:05:20,750 --> 01:05:22,666 I'm going to go ahead and return the return 108 01:05:22,666 --> 01:05:25,541 value of input, this time prompting the user for their house. 109 01:05:25,541 --> 01:05:27,250 And I need one final detail. 110 01:05:27,250 --> 01:05:32,208 At the very bottom, let's continue our habit of doing if the name of this file 111 01:05:32,208 --> 01:05:35,750 equals, equals, quote, unquote, main, then let's go ahead 112 01:05:35,750 --> 01:05:38,875 and actually call main and recall that we have that in place 113 01:05:38,875 --> 01:05:42,458 so that, if this eventually becomes part of a module, a library of sorts, 114 01:05:42,458 --> 01:05:44,791 I don't accidentally call main blindly. 115 01:05:44,791 --> 01:05:50,250 I only do it if I mean to run main from the command line on this file. 116 01:05:50,250 --> 01:05:53,458 All right, so if I didn't make any mistakes here, let me go ahead 117 01:05:53,458 --> 01:05:57,416 and, in my terminal window, again, run Python of student.py, Enter. 118 01:05:57,416 --> 01:05:59,250 Let's type in Harry, Enter. 119 01:05:59,250 --> 01:06:01,541 Let's type in Gryffindor, Enter. 120 01:06:01,541 --> 01:06:02,875 And we're set. 121 01:06:02,875 --> 01:06:05,583 Harry from Gryffindor seems to still be working. 122 01:06:05,583 --> 01:06:09,416 So we haven't really solved the problem any more correctly, 123 01:06:09,416 --> 01:06:11,875 but I've laid the foundation to maybe now do 124 01:06:11,875 --> 01:06:13,583 some more interesting things because I've 125 01:06:13,583 --> 01:06:16,875 had these building blocks in place. 126 01:06:16,875 --> 01:06:21,083 But let me propose that we could be doing this a little bit differently. 127 01:06:21,083 --> 01:06:23,000 get_name, get_house is fine. 128 01:06:23,000 --> 01:06:26,416 But at the end of the day, I'm really trying to get a student from the user. 129 01:06:26,416 --> 01:06:30,083 I want their name and their house, not just one or the other. 130 01:06:30,083 --> 01:06:32,833 So maybe it would be a little cleaner still 131 01:06:32,833 --> 01:06:36,083 to define a function called get_student and let 132 01:06:36,083 --> 01:06:38,291 get_student do all of this work for us. 133 01:06:38,291 --> 01:06:43,000 Now, theoretically, get_student could call get_name and could call get_house. 134 01:06:43,000 --> 01:06:44,750 But because these functions are so short, 135 01:06:44,750 --> 01:06:48,875 I think I'm OK with just defining one function, called get_student, 136 01:06:48,875 --> 01:06:50,666 that similarly won't take any arguments. 137 01:06:50,666 --> 01:06:52,250 But it's going to do two things. 138 01:06:52,250 --> 01:06:55,875 It's going to get the students name, by prompting them with input as before. 139 01:06:55,875 --> 01:06:57,625 And it's going to get the student's house, 140 01:06:57,625 --> 01:07:01,125 by also prompting them as before. 141 01:07:01,125 --> 01:07:02,416 Now, hmm. 142 01:07:02,416 --> 01:07:04,833 I want to return the student, but I think 143 01:07:04,833 --> 01:07:06,875 I might have painted myself into a corner 144 01:07:06,875 --> 01:07:10,125 here because I now have two variables-- name and house. 145 01:07:10,125 --> 01:07:13,250 And yet, up until now, we've pretty much returned one or the other. 146 01:07:13,250 --> 01:07:15,000 We've returned one value. 147 01:07:15,000 --> 01:07:17,500 So any suggestions for how we can perhaps 148 01:07:17,500 --> 01:07:22,375 solve this problem that I just created for myself, whereby I want to return, 149 01:07:22,375 --> 01:07:28,083 really, a student, but I currently have a name variable and a house variable. 150 01:07:28,083 --> 01:07:30,541 I'd minimally like to return both of those. 151 01:07:30,541 --> 01:07:33,208 AUDIENCE: I believe that, we can return a dictionary, 152 01:07:33,208 --> 01:07:35,000 includes the name and the house. 153 01:07:35,000 --> 01:07:36,833 DAVID J. MALAN: Yeah, so we absolutely could 154 01:07:36,833 --> 01:07:41,000 return a dictionary-- a dict object in Python, whereby maybe one key is name; 155 01:07:41,000 --> 01:07:43,666 one key is house; and the values thereof are exactly 156 01:07:43,666 --> 01:07:44,916 the values of these variables. 157 01:07:44,916 --> 01:07:46,791 So we could totally do that. 158 01:07:46,791 --> 01:07:49,375 I worry that that might be getting a little complicated. 159 01:07:49,375 --> 01:07:51,916 I wonder if there's a simpler way instead. 160 01:07:51,916 --> 01:07:55,000 Any other instincts-- even if you're not sure it would work? 161 01:07:55,000 --> 01:07:56,541 AUDIENCE: Return both name and house? 162 01:07:56,541 --> 01:07:58,333 DAVID J. MALAN: Return both name and house. 163 01:07:58,333 --> 01:07:59,416 I like the sound of that. 164 01:07:59,416 --> 01:08:00,250 It sounds simple. 165 01:08:00,250 --> 01:08:03,041 I don't have to figure out what a dictionary is going to look like. 166 01:08:03,041 --> 01:08:05,750 And in fact, this, too, would be a valid approach, 167 01:08:05,750 --> 01:08:07,250 even if you've not seen this before. 168 01:08:07,250 --> 01:08:11,625 It turns out, in Python, that you can return multiple values, 169 01:08:11,625 --> 01:08:12,958 but that's a bit of a white lie. 170 01:08:12,958 --> 01:08:16,333 Or we could take Muhammad's approach of actually returning a dictionary 171 01:08:16,333 --> 01:08:17,833 and putting multiple keys therein. 172 01:08:17,833 --> 01:08:19,833 So here, again, we have yet another example 173 01:08:19,833 --> 01:08:22,375 of how you can solve the same problem in at least two ways, 174 01:08:22,375 --> 01:08:24,708 and I daresay we're about to see even more. 175 01:08:24,708 --> 01:08:27,916 So one way you could solve this problem, whereby 176 01:08:27,916 --> 01:08:32,916 you want to return multiple values, would be to do something like this. 177 01:08:32,916 --> 01:08:37,666 I could go ahead and literally return not just name, but I could put a comma 178 01:08:37,666 --> 01:08:39,250 and also return house. 179 01:08:39,250 --> 01:08:41,958 This is not necessarily something you can do in other languages 180 01:08:41,958 --> 01:08:44,166 if you have programmed in other languages before. 181 01:08:44,166 --> 01:08:45,416 It depends on the language. 182 01:08:45,416 --> 01:08:49,291 But it looks like, thanks to this comma, maybe I can, in fact, 183 01:08:49,291 --> 01:08:51,916 return two values as [INAUDIBLE] proposed. 184 01:08:51,916 --> 01:08:55,375 Well, if I'm returning to values in this way on line 10, 185 01:08:55,375 --> 01:08:57,916 how do I get both values at the same time? 186 01:08:57,916 --> 01:08:59,208 Well, there's a couple of ways. 187 01:08:59,208 --> 01:09:01,833 Let me go up to my main function. 188 01:09:01,833 --> 01:09:04,416 I know, minimally, I'm going to have to change the get_name 189 01:09:04,416 --> 01:09:05,958 and get_house to get_student. 190 01:09:05,958 --> 01:09:09,041 But what am I going to store the return value in? 191 01:09:09,041 --> 01:09:11,500 I think I could actually do this. 192 01:09:11,500 --> 01:09:14,916 And we have seen this technique before, where you can unpack, 193 01:09:14,916 --> 01:09:17,541 so to speak, sequences of values that are coming back. 194 01:09:17,541 --> 01:09:21,083 And indeed, consider this to be exactly that. name, 195 01:09:21,083 --> 01:09:25,000 house is some kind of sequence that I'm returning of values-- name, house. 196 01:09:25,000 --> 01:09:27,291 So if I want to unpack those and store the return 197 01:09:27,291 --> 01:09:30,000 values in two separate variables, I can, in fact, 198 01:09:30,000 --> 01:09:34,208 use the commas on the left-hand side of my assignment operator, the equal sign, 199 01:09:34,208 --> 01:09:35,416 to do just that. 200 01:09:35,416 --> 01:09:40,000 Now, to be clear, I don't need to call these variables name and house here. 201 01:09:40,000 --> 01:09:43,500 I could simplify this and use just n here and h here, 202 01:09:43,500 --> 01:09:46,583 and then I could return just n and h. 203 01:09:46,583 --> 01:09:51,208 But I would argue that's not very clear to the reader as to what's going on. 204 01:09:51,208 --> 01:09:52,958 So I think, in this case, even though it's 205 01:09:52,958 --> 01:09:56,000 a coincidence that I've used the same variable names in get_student 206 01:09:56,000 --> 01:09:59,875 and get_name, and in main, it's a little more readable to someone like me. 207 01:09:59,875 --> 01:10:01,541 So I'm going to leave it as is. 208 01:10:01,541 --> 01:10:04,041 Well, let's go ahead and see, now, if this works. 209 01:10:04,041 --> 01:10:07,166 Let me clear my screen down here and run Python of student.py, Enter. 210 01:10:07,166 --> 01:10:08,375 Let's again type in Harry. 211 01:10:08,375 --> 01:10:11,041 Let's again type in Gryffindor, Enter. 212 01:10:11,041 --> 01:10:14,791 And voila, we still see that Harry is from Gryffindor. 213 01:10:14,791 --> 01:10:16,958 But what are we actually doing here? 214 01:10:16,958 --> 01:10:19,666 What are we actually doing by returning this value? 215 01:10:19,666 --> 01:10:24,333 Well, it turns out that what we've just done is use a tuple. 216 01:10:24,333 --> 01:10:29,791 A tuple is another type of data in Python that's a collection of values-- 217 01:10:29,791 --> 01:10:32,291 x, y or x, y, z. 218 01:10:32,291 --> 01:10:36,750 It's similar in spirit to a list, in that sense, but it's immutable. 219 01:10:36,750 --> 01:10:37,666 It's not mutable. 220 01:10:37,666 --> 01:10:38,708 Now, what does that mean? 221 01:10:38,708 --> 01:10:41,708 A list, as we've seen it before, is a data structure in Python 222 01:10:41,708 --> 01:10:43,458 that you can change the values of. 223 01:10:43,458 --> 01:10:47,250 You can go into bracket 0 for the first location and change the value there. 224 01:10:47,250 --> 01:10:50,208 You can go to bracket 1, bracket 2, bracket 3 and actually change 225 01:10:50,208 --> 01:10:51,458 the values in lists. 226 01:10:51,458 --> 01:10:54,791 But if you have no intention of changing the values of variables 227 01:10:54,791 --> 01:10:57,750 and you want to return, effectively, multiple values, 228 01:10:57,750 --> 01:10:59,541 you don't have to even return it as a list. 229 01:10:59,541 --> 01:11:03,416 You can return it as a tuple instead, just by using a comma. 230 01:11:03,416 --> 01:11:07,000 And it turns out we can make explicit that-- here's the white lie. 231 01:11:07,000 --> 01:11:10,500 I'm not actually returning to values per se. 232 01:11:10,500 --> 01:11:13,416 Whenever you use a comma in this way on line 9, 233 01:11:13,416 --> 01:11:17,500 you're actually returning one value, which is a tuple. 234 01:11:17,500 --> 01:11:20,291 Inside of that tuple now are two values. 235 01:11:20,291 --> 01:11:23,791 So it's similar in spirit to returning one list with two thing Here 236 01:11:23,791 --> 01:11:26,291 I'm returning one tuple with two things. 237 01:11:26,291 --> 01:11:28,958 And the mere fact that I've used a comma and nothing else 238 01:11:28,958 --> 01:11:31,375 tells Python that I indeed want to return a tuple. 239 01:11:31,375 --> 01:11:34,083 But there's more explicit syntax that we can use instead. 240 01:11:34,083 --> 01:11:39,291 I can actually-- more verbosely-- put explicit parentheses around the values 241 01:11:39,291 --> 01:11:42,458 of this tuple just to make more clear to me, to the reader 242 01:11:42,458 --> 01:11:44,083 that this isn't two values per se. 243 01:11:44,083 --> 01:11:46,500 This is one value with two things inside of it. 244 01:11:46,500 --> 01:11:48,458 And what I can actually do then, too, is-- 245 01:11:48,458 --> 01:11:51,000 I don't have to unpack this up here, so to speak. 246 01:11:51,000 --> 01:11:54,833 I can actually go up here and maybe give a more apt name, like student, 247 01:11:54,833 --> 01:11:58,375 and I can name the value, or rather name the variable 248 01:11:58,375 --> 01:12:01,333 in which I'm storing the return value of get_student as, 249 01:12:01,333 --> 01:12:02,791 quote, unquote, "student." 250 01:12:02,791 --> 01:12:05,166 So maybe this is a little better design now 251 01:12:05,166 --> 01:12:08,625 because I'm sort of abstracting away what a student is. 252 01:12:08,625 --> 01:12:11,666 It's implemented at the moment as a tuple with two values. 253 01:12:11,666 --> 01:12:15,875 But at least, now I have a variable called what I mean, a student. 254 01:12:15,875 --> 01:12:17,375 But there's going to be a catch. 255 01:12:17,375 --> 01:12:21,583 On line 3, I still want to print out that student's name and their house. 256 01:12:21,583 --> 01:12:24,541 But I don't have a name variable anymore, and I don't have a house. 257 01:12:24,541 --> 01:12:28,625 And I also don't have a dictionary, as was proposed earlier, so I can't even 258 01:12:28,625 --> 01:12:31,833 go at those keys by name. 259 01:12:31,833 --> 01:12:35,166 But what a tuple is-- it's very similar in spirit to a list, 260 01:12:35,166 --> 01:12:37,000 but it is indeed just immutable. 261 01:12:37,000 --> 01:12:39,791 And what I mean by that is I can still index into it 262 01:12:39,791 --> 01:12:45,041 numerically by saying student [0] for the item 263 01:12:45,041 --> 01:12:47,000 in the first location in that tuple. 264 01:12:47,000 --> 01:12:50,416 And then over here, instead of house, I can say student [1]. 265 01:12:50,416 --> 01:12:54,291 student [1] is going to give me the second location in that tuple. 266 01:12:54,291 --> 01:12:56,166 Let me go ahead and clear my terminal window. 267 01:12:56,166 --> 01:12:58,083 Again, run Python of student.py. 268 01:12:58,083 --> 01:12:59,250 Let's type in Harry. 269 01:12:59,250 --> 01:13:04,500 Let's type in Gryffindor, Enter, and we still have some working code. 270 01:13:04,500 --> 01:13:07,208 Let me pause here now and see if there are 271 01:13:07,208 --> 01:13:11,500 any questions on this technique of returning a tuple 272 01:13:11,500 --> 01:13:14,750 and indexing into it in this way. 273 01:13:14,750 --> 01:13:17,125 AUDIENCE: I guess, what's an actual use case where 274 01:13:17,125 --> 01:13:21,333 you would use a tuple versus a list or something else that's similar? 275 01:13:21,333 --> 01:13:23,166 DAVID J. MALAN: It's a really good question. 276 01:13:23,166 --> 01:13:25,291 When would you use a tuple versus a list? 277 01:13:25,291 --> 01:13:28,208 When you want to program defensively, or, in general, 278 01:13:28,208 --> 01:13:31,375 when you know that the values in this variable shouldn't change, 279 01:13:31,375 --> 01:13:34,333 so why would you use a data type that allows them to be changed? 280 01:13:34,333 --> 01:13:37,791 It just invites mistakes, bugs down the line, either 281 01:13:37,791 --> 01:13:40,666 by you or colleagues who are interacting with your code. 282 01:13:40,666 --> 01:13:44,083 So tuple is just another way where you can increase 283 01:13:44,083 --> 01:13:47,708 the probability of correctness by just not letting anyone, yourself included, 284 01:13:47,708 --> 01:13:49,750 change the contents therein. 285 01:13:49,750 --> 01:13:52,250 So it's just another tool in your toolkit. 286 01:13:52,250 --> 01:13:55,333 But let's make clear, then, what I mean by "immutable." 287 01:13:55,333 --> 01:13:58,833 Again, I claim that "immutable" means that you cannot change the value. 288 01:13:58,833 --> 01:14:01,000 Well, let's go ahead and try to do this. 289 01:14:01,000 --> 01:14:03,750 Let me go ahead and run this program once more as is-- 290 01:14:03,750 --> 01:14:05,291 Python of student.py. 291 01:14:05,291 --> 01:14:09,416 Let me go ahead and type in, for instance-- 292 01:14:09,416 --> 01:14:11,375 how about Padma's name? 293 01:14:11,375 --> 01:14:13,208 And I'm going to go ahead and say that Padma 294 01:14:13,208 --> 01:14:14,916 is in Gryffindor as in the movies. 295 01:14:14,916 --> 01:14:16,958 And we see-- Padma from Gryffindor. 296 01:14:16,958 --> 01:14:20,791 But technically, I went down this rabbit hole in looking at Harry Potter 297 01:14:20,791 --> 01:14:21,541 more closely. 298 01:14:21,541 --> 01:14:25,000 Technically, in the books, Padma, I believe, was from Ravenclaw. 299 01:14:25,000 --> 01:14:27,083 So this is actually a mistake or an inconsistency 300 01:14:27,083 --> 01:14:28,750 between the movies and the books. 301 01:14:28,750 --> 01:14:31,625 Let's see if we can't fix this inconsistency in our code. 302 01:14:31,625 --> 01:14:33,208 So how about we do this? 303 01:14:33,208 --> 01:14:36,333 If the student's name that's inputted equals Padma, 304 01:14:36,333 --> 01:14:40,208 why don't we override whatever the house is 305 01:14:40,208 --> 01:14:42,875 and change it to be properly Gryffindor. 306 01:14:42,875 --> 01:14:46,083 Let me go ahead and do if student-- 307 01:14:46,083 --> 01:14:49,708 now, if I want to get at Padma's name, I'm going to have to do student [0]. 308 01:14:49,708 --> 01:14:52,625 I have to know what location the name is in in this tuple. 309 01:14:52,625 --> 01:14:57,750 But if that value equals equals Padma, let's go ahead with this if statement 310 01:14:57,750 --> 01:14:58,666 and make a change. 311 01:14:58,666 --> 01:15:02,208 Let's change the student's [1] value. 312 01:15:02,208 --> 01:15:04,625 So the second value, if we're zero indexing-- 313 01:15:04,625 --> 01:15:07,375 let's change it to be another house in the world of Harry Potter 314 01:15:07,375 --> 01:15:08,916 called Ravenclaw. 315 01:15:08,916 --> 01:15:11,750 So I'm just fixing maybe the user's input. 316 01:15:11,750 --> 01:15:14,166 They watched the movie so they type in Padma Gryffindor, 317 01:15:14,166 --> 01:15:17,333 but, mm-mm, in the books, it was Padma from Ravenclaw. 318 01:15:17,333 --> 01:15:20,250 All right, let me go ahead and go down to my terminal window, 319 01:15:20,250 --> 01:15:24,041 clear my terminal, and do Python of student.py, Enter. 320 01:15:24,041 --> 01:15:27,000 I'm going to do Harry as well as Gryffindor, just to demonstrate 321 01:15:27,000 --> 01:15:29,750 that that is still working as intended. 322 01:15:29,750 --> 01:15:33,958 Let me clear my screen again, though, and run Python of student.py on Padma, 323 01:15:33,958 --> 01:15:37,500 and I'll put her, too, in Gryffindor, as in the movies, and hit Enter. 324 01:15:37,500 --> 01:15:40,916 And now I just see a big mess of errors on the screen. 325 01:15:40,916 --> 01:15:42,708 Some kind of exception has been thrown. 326 01:15:42,708 --> 01:15:45,166 And indeed, a type error has happened. 327 01:15:45,166 --> 01:15:48,750 I'm using a data type wherein there's an error, and what is that error? 328 01:15:48,750 --> 01:15:53,041 Well, 'tuple' object does not support item assignment. 329 01:15:53,041 --> 01:15:57,375 It's a little arcanely expressed-- that is, it's not really very user friendly. 330 01:15:57,375 --> 01:15:59,250 But if you think about what those words mean, 331 01:15:59,250 --> 01:16:01,458 'tuple" object does not support item assignment. 332 01:16:01,458 --> 01:16:03,458 So assignment is copying from right to left. 333 01:16:03,458 --> 01:16:05,208 So somehow, that's invalid. 334 01:16:05,208 --> 01:16:10,250 And here is a manifestation of the immutability of tuples. 335 01:16:10,250 --> 01:16:14,208 You cannot change location 0 or 1 or anything inside. 336 01:16:14,208 --> 01:16:15,250 That is a feature. 337 01:16:15,250 --> 01:16:17,250 That is the design of a tuple. 338 01:16:17,250 --> 01:16:20,000 So if I want to override that, I think I'm 339 01:16:20,000 --> 01:16:23,125 going to have to use a different type of data that we've used before-- 340 01:16:23,125 --> 01:16:24,750 namely a list, and that's fine. 341 01:16:24,750 --> 01:16:28,416 If you want to enable yourself and colleagues using your code 342 01:16:28,416 --> 01:16:31,083 to change the contents of that container, 343 01:16:31,083 --> 01:16:34,375 well, we can go ahead and return not a tuple using 344 01:16:34,375 --> 01:16:37,541 explicit parentheses or no parentheses, just the comma, 345 01:16:37,541 --> 01:16:39,250 but I can use square brackets. 346 01:16:39,250 --> 01:16:41,916 And if I'm using square brackets on the left and the right, 347 01:16:41,916 --> 01:16:44,458 this is indeed explicitly a list. 348 01:16:44,458 --> 01:16:47,041 Same idea, but it's mutable. 349 01:16:47,041 --> 01:16:50,000 That is to say you can change the contents of a list. 350 01:16:50,000 --> 01:16:53,125 So making no other changes, just returning a list 351 01:16:53,125 --> 01:16:57,833 with square brackets instead of a tuple with parentheses or just the comma. 352 01:16:57,833 --> 01:17:01,041 Let me go ahead now and run Python of student.py, Enter. 353 01:17:01,041 --> 01:17:04,000 Let me type in Harry and Gryffindor again. 354 01:17:04,000 --> 01:17:05,458 That's still working. 355 01:17:05,458 --> 01:17:06,416 Good to see. 356 01:17:06,416 --> 01:17:11,416 Let me run this once more and type in Padma and Gryffindor, as in the movies, 357 01:17:11,416 --> 01:17:15,333 but no, now we've corrected it to be Padma from Ravenclaw, 358 01:17:15,333 --> 01:17:17,958 as in the books instead. 359 01:17:17,958 --> 01:17:22,541 Any questions now on tuples versus lists or this idea 360 01:17:22,541 --> 01:17:26,541 of immutability versus mutability. 361 01:17:26,541 --> 01:17:31,833 AUDIENCE: Can we use a nested tuple in Python, like a nest list? 362 01:17:31,833 --> 01:17:32,958 DAVID J. MALAN: Absolutely. 363 01:17:32,958 --> 01:17:35,250 You can have not only nested lists in Python, 364 01:17:35,250 --> 01:17:38,291 where one of the elements in a list could be another list-- so you 365 01:17:38,291 --> 01:17:41,500 have some square brackets out here; you might have some other square brackets 366 01:17:41,500 --> 01:17:42,041 inside. 367 01:17:42,041 --> 01:17:44,833 You can absolutely do the same with a tuple as well. 368 01:17:44,833 --> 01:17:47,833 There is no constraint on the types of values you can put in there. 369 01:17:47,833 --> 01:17:50,125 We've not had occasion to do that in this case. 370 01:17:50,125 --> 01:17:54,041 I'm just returning a simple tuple with two elements. 371 01:17:54,041 --> 01:17:56,291 But yes, you could absolutely do that, too. 372 01:17:56,291 --> 01:17:59,416 Other questions on tuples versus lists? 373 01:17:59,416 --> 01:18:03,208 AUDIENCE: OK, for example, when I see the square brackets, 374 01:18:03,208 --> 01:18:05,250 is it mainly used for the list? 375 01:18:05,250 --> 01:18:07,041 DAVID J. MALAN: Oh, a really good question. 376 01:18:07,041 --> 01:18:07,875 Sort of. 377 01:18:07,875 --> 01:18:12,250 So when you create a value like a list, you use square brackets, 378 01:18:12,250 --> 01:18:14,041 and that would indeed be a visual indicator 379 01:18:14,041 --> 01:18:16,458 that this is definitely a list. 380 01:18:16,458 --> 01:18:19,041 If you instead see parentheses, that's a visual indicator, 381 01:18:19,041 --> 01:18:21,708 when creating a value, that it's definitely a tuple. 382 01:18:21,708 --> 01:18:26,416 However, somewhat confusingly, both lists and tuples 383 01:18:26,416 --> 01:18:30,375 use square brackets when you access the contents of them. 384 01:18:30,375 --> 01:18:34,000 When you index into them at location 0 or location 1 385 01:18:34,000 --> 01:18:36,083 you always use square brackets. 386 01:18:36,083 --> 01:18:37,500 So that's the distinction there. 387 01:18:37,500 --> 01:18:38,875 Good question. 388 01:18:38,875 --> 01:18:42,916 Allow me to propose now, if I may, that we solve this problem yet another way, 389 01:18:42,916 --> 01:18:46,250 and let's see if we're either making things better or for worse than us. 390 01:18:46,250 --> 01:18:49,916 Recall that dictionaries, or dict objects, also exist in Python. 391 01:18:49,916 --> 01:18:53,250 And a dictionary is this collection of keys and values. 392 01:18:53,250 --> 01:18:55,458 And the upside, in particular, of a dictionary 393 01:18:55,458 --> 01:18:57,083 is that they have better semantics. 394 01:18:57,083 --> 01:18:59,625 You don't just have to assume that a name is always 395 01:18:59,625 --> 01:19:03,291 going to be at location 0; house is always going to be at location 1. 396 01:19:03,291 --> 01:19:06,916 That's the kind of thing, especially if you had three, four, or more values-- 397 01:19:06,916 --> 01:19:10,000 eventually you or someone is going to get confused and forget 398 01:19:10,000 --> 01:19:12,958 what the order is and you're going to write buggy code. 399 01:19:12,958 --> 01:19:15,333 So a dictionary is a little more powerful 400 01:19:15,333 --> 01:19:19,000 in that you can semantically associate keys, little descriptions, 401 01:19:19,000 --> 01:19:20,041 with the values-- 402 01:19:20,041 --> 01:19:22,541 those keys and those values, respectively. 403 01:19:22,541 --> 01:19:25,666 So let me go ahead and do this, and we can do this in a few different ways. 404 01:19:25,666 --> 01:19:28,958 But let me propose that we focus on get_student here . 405 01:19:28,958 --> 01:19:30,583 And let's go ahead and do this. 406 01:19:30,583 --> 01:19:35,875 Let me go ahead and delete the implementation of get_student as is. 407 01:19:35,875 --> 01:19:40,083 Let me create a student variable and initialize it to an empty dictionary. 408 01:19:40,083 --> 01:19:43,083 And I can do that with just two curly braces here. 409 01:19:43,083 --> 01:19:46,958 And then let me go ahead and set two keys inside of that dictionary. 410 01:19:46,958 --> 01:19:49,708 Inside of the student, there will be, quote unquote, a "name" 411 01:19:49,708 --> 01:19:53,625 key, and the value of that is going to be whatever the return value of input 412 01:19:53,625 --> 01:19:55,625 is when I prompt the user for their name. 413 01:19:55,625 --> 01:19:59,708 And then the "house" key, inside of that same student dictionary, 414 01:19:59,708 --> 01:20:02,750 is going to be the return value of whatever the user types 415 01:20:02,750 --> 01:20:07,000 in for their house. , And lastly I'm going to go ahead and return student. 416 01:20:07,000 --> 01:20:11,000 So now I am literally returning one thing, still, 417 01:20:11,000 --> 01:20:15,291 but this time, it's a dict rather than a tuple, rather than a list. 418 01:20:15,291 --> 01:20:18,041 But there are still two things in it, technically four things 419 01:20:18,041 --> 01:20:19,708 if you count the keys and the values. 420 01:20:19,708 --> 01:20:22,083 But there's two key value pairs. 421 01:20:22,083 --> 01:20:24,625 Now, my code up here is going to have to change a little bit. 422 01:20:24,625 --> 01:20:27,375 And let's simplify this and remove, for instance, now 423 01:20:27,375 --> 01:20:31,458 the Padma if statement just to focus on what's changing at hand. 424 01:20:31,458 --> 01:20:34,000 And let me go ahead now and leave line 2 alone. 425 01:20:34,000 --> 01:20:37,708 I'm still going to have a student variable that gets assigned the return 426 01:20:37,708 --> 01:20:39,166 value of get_student. 427 01:20:39,166 --> 01:20:45,375 But what I want to do here now is actually access the keys inside of that 428 01:20:45,375 --> 01:20:49,083 dictionary-- not by numeric index, which was for tuples and lists-- 429 01:20:49,083 --> 01:20:52,541 0 and 1, but by way of the keys. 430 01:20:52,541 --> 01:20:55,291 Now, normally, I might be in the habit, as I personally 431 01:20:55,291 --> 01:20:58,916 am, of using double quotes-- quote, unquote, "name" inside of there 432 01:20:58,916 --> 01:21:02,166 and quote, unquote, "house" inside of there. 433 01:21:02,166 --> 01:21:05,833 But before I even run this code and show you a mistake-- 434 01:21:05,833 --> 01:21:08,750 see an error on the screen, does anyone want to call out 435 01:21:08,750 --> 01:21:10,666 what I have done wrong here? 436 01:21:10,666 --> 01:21:12,375 This is just an f string. 437 01:21:12,375 --> 01:21:16,708 I just want to print out the value of the name key, the value of the house 438 01:21:16,708 --> 01:21:18,666 key in this dictionary. 439 01:21:18,666 --> 01:21:21,083 AUDIENCE: [INAUDIBLE] 440 01:21:21,083 --> 01:21:21,333 441 01:21:21,333 --> 01:21:23,625 DAVID J. MALAN: Your audio was a little garbled for us. 442 01:21:23,625 --> 01:21:26,250 But I think I heard double quotes and single quotes. 443 01:21:26,250 --> 01:21:29,708 So I'm going to assume that, indeed, you've identified precisely the issue. 444 01:21:29,708 --> 01:21:31,541 I'm just going to confuse Python right now. 445 01:21:31,541 --> 01:21:35,833 Even though this is an f string inside of double quotes, prefixed with an f, 446 01:21:35,833 --> 01:21:39,000 I can't actually use my double quotes inside my double quotes 447 01:21:39,000 --> 01:21:41,125 because that's going to potentially confuse Python. 448 01:21:41,125 --> 01:21:44,375 If I run this program now, Python of student.py and hit 449 01:21:44,375 --> 01:21:46,166 Enter, I get a syntax error. 450 01:21:46,166 --> 01:21:48,000 So the program didn't even run fully. 451 01:21:48,000 --> 01:21:51,958 It just couldn't be understood because it got confused by those double quotes. 452 01:21:51,958 --> 01:21:54,041 So the simplest fix here would indeed just 453 01:21:54,041 --> 01:21:58,250 be to use not double quotes but single quotes around the keys, 454 01:21:58,250 --> 01:22:02,041 or conversely, flip the double quotes on the outside to single quotes, 455 01:22:02,041 --> 01:22:03,583 then use double quotes in the inside. 456 01:22:03,583 --> 01:22:05,125 You just want to be consistent. 457 01:22:05,125 --> 01:22:09,041 So a subtle detail, but again, this is now specific to dictionary syntax. 458 01:22:09,041 --> 01:22:14,666 This isn't fundamental to how we're solving this current problem at hand. 459 01:22:14,666 --> 01:22:16,083 Well, let's go ahead and try this. 460 01:22:16,083 --> 01:22:18,416 Let me go ahead now and run Python of student.py. 461 01:22:18,416 --> 01:22:19,833 Let's go ahead and type in Harry. 462 01:22:19,833 --> 01:22:20,916 Let's type in Gryffindor. 463 01:22:20,916 --> 01:22:24,083 And hopefully, Harry is back from Gryffindor. 464 01:22:24,083 --> 01:22:25,375 No syntax errors. 465 01:22:25,375 --> 01:22:26,250 No other errors. 466 01:22:26,250 --> 01:22:28,000 I think I'm back in business here. 467 01:22:28,000 --> 01:22:31,125 And what I do like to be clear about using a dictionary 468 01:22:31,125 --> 01:22:34,000 is that it's allowing me just better semantics. 469 01:22:34,000 --> 01:22:38,958 And again, I don't have to remember, memorize, document that 0 is name; 470 01:22:38,958 --> 01:22:40,166 1 is house. 471 01:22:40,166 --> 01:22:43,291 Instead, "name" is name and "house" is house. 472 01:22:43,291 --> 01:22:45,500 It's just a little clearer, a little more expressive. 473 01:22:45,500 --> 01:22:49,250 So that's generally a good thing, especially if we stored more data about 474 01:22:49,250 --> 01:22:53,000 students than just their name and their house-- if you had three fields, four, 475 01:22:53,000 --> 01:22:54,625 five, 10 different fields-- 476 01:22:54,625 --> 01:22:58,875 no one's going to want to remember or be able to remember forever which is zero 477 01:22:58,875 --> 01:23:00,625 which is 1, which is 2, and so forth. 478 01:23:00,625 --> 01:23:04,958 Better to introduce names, like "name" and "house" in this case. 479 01:23:04,958 --> 01:23:07,125 But let me tighten this up further. 480 01:23:07,125 --> 01:23:10,541 I'm typically in the habit of not introducing variables unnecessarily, 481 01:23:10,541 --> 01:23:12,250 unless they make the code more readable. 482 01:23:12,250 --> 01:23:15,958 And an alternative way to format the same code would be this. 483 01:23:15,958 --> 01:23:19,333 Strictly speaking, I don't need to create an empty dictionary, 484 01:23:19,333 --> 01:23:23,375 then add one key to it, then add a second key to it, 485 01:23:23,375 --> 01:23:25,375 and then return that dictionary. 486 01:23:25,375 --> 01:23:29,458 I can actually consolidate this all into one statement, if you will. 487 01:23:29,458 --> 01:23:30,958 Let me go ahead and do this. 488 01:23:30,958 --> 01:23:35,458 Let me go ahead and say name equals inputs return value, 489 01:23:35,458 --> 01:23:38,583 house equals inputs return value, and then, 490 01:23:38,583 --> 01:23:41,666 instead of returning any variable name student, which I'm going to propose 491 01:23:41,666 --> 01:23:44,208 doesn't need to exist anymore, let me just create 492 01:23:44,208 --> 01:23:46,416 and return the dictionary all at once. 493 01:23:46,416 --> 01:23:50,458 Let me do, quote, unquote, "name" in lowercase here, and then the variable. 494 01:23:50,458 --> 01:23:52,041 It's storing the user's name. 495 01:23:52,041 --> 01:23:55,208 Then, quote, unquote, "house," as my second key, the value of which 496 01:23:55,208 --> 01:23:57,083 is going to be house, the variable. 497 01:23:57,083 --> 01:23:58,166 Now, is this better? 498 01:23:58,166 --> 01:23:59,000 Maybe, maybe not. 499 01:23:59,000 --> 01:24:00,958 Maybe the first way was a little more readable, 500 01:24:00,958 --> 01:24:03,416 and that's totally fine to create variables 501 01:24:03,416 --> 01:24:05,833 if they improve the readability of your code. 502 01:24:05,833 --> 01:24:09,833 But just know that you can also create and return a dictionary on the fly 503 01:24:09,833 --> 01:24:12,208 like this, so to speak, all in one line, and I 504 01:24:12,208 --> 01:24:14,333 think it's arguably pretty reasonable in this case. 505 01:24:14,333 --> 01:24:14,875 Why? 506 01:24:14,875 --> 01:24:16,083 It's just pretty short. 507 01:24:16,083 --> 01:24:18,875 I probably wouldn't do this if it got longer and longer and longer. 508 01:24:18,875 --> 01:24:22,583 I might minimally then start moving my key value pairs to separate lines. 509 01:24:22,583 --> 01:24:27,458 But this would just be a slightly more compact way of doing this as well. 510 01:24:27,458 --> 01:24:29,375 But let me propose we do one more change. 511 01:24:29,375 --> 01:24:32,958 Let's go ahead and introduce that same special casing of Padma 512 01:24:32,958 --> 01:24:36,458 to fix her house from Gryffindor, for instance, to Ravenclaw. 513 01:24:36,458 --> 01:24:38,250 How do we do this with dictionaries? 514 01:24:38,250 --> 01:24:41,625 Well, dictionaries, like lists, are mutable. 515 01:24:41,625 --> 01:24:45,416 You can change what is in them, just like you can lists. 516 01:24:45,416 --> 01:24:46,250 How do you do that? 517 01:24:46,250 --> 01:24:48,041 It's just a little different syntactically. 518 01:24:48,041 --> 01:24:50,291 So let's go back into main and do this fix. 519 01:24:50,291 --> 01:24:57,708 If the student variable has a name key that equals equals Padma, then, 520 01:24:57,708 --> 01:25:04,375 indented, go ahead and change the value of the house key inside of that student 521 01:25:04,375 --> 01:25:08,666 dictionary to be, quote, unquote, "Ravenclaw" instead. 522 01:25:08,666 --> 01:25:11,416 So very similar in spirit to what we did with a list. 523 01:25:11,416 --> 01:25:16,250 But instead of using location 0 and 1, we're much more clearly, explicitly, 524 01:25:16,250 --> 01:25:19,375 semantically using, quote, unquote, "name" and, quote, unquote, 525 01:25:19,375 --> 01:25:24,250 "house," because you index into lists and tuples using numbers, but you index 526 01:25:24,250 --> 01:25:27,916 into dictionaries using strings, as I've done here. 527 01:25:27,916 --> 01:25:31,250 All right, let me go ahead and run Python of student.py. 528 01:25:31,250 --> 01:25:33,208 We'll again do Harry from Gryffindor. 529 01:25:33,208 --> 01:25:35,125 And I think all is well. 530 01:25:35,125 --> 01:25:38,708 Let me run it one more time this time with Padma, who, in the movies, 531 01:25:38,708 --> 01:25:43,375 is from Gryffindor, but should really be from Ravenclaw. 532 01:25:43,375 --> 01:25:49,250 Any questions then on this progression from tuples to lists to dictionaries? 533 01:25:49,250 --> 01:25:53,458 We haven't necessarily introduced anything new, other than those tuples, 534 01:25:53,458 --> 01:25:56,250 which have been available to us all this time. 535 01:25:56,250 --> 01:26:00,000 But the goal at the moment is just to demonstrate this distinction 536 01:26:00,000 --> 01:26:02,250 among these different data types and how they 537 01:26:02,250 --> 01:26:04,333 each work a little bit differently. 538 01:26:04,333 --> 01:26:10,416 AUDIENCE: What if a combination of lists is there in our tuple? 539 01:26:10,416 --> 01:26:16,125 We can change the list because are immutable but lists are mutable? 540 01:26:16,125 --> 01:26:17,125 DAVID J. MALAN: Correct. 541 01:26:17,125 --> 01:26:19,291 You can change the contents of lists, and you 542 01:26:19,291 --> 01:26:23,125 can put most anything you want in them-- other lists or strings, as I've done, 543 01:26:23,125 --> 01:26:24,583 integers, or anything else. 544 01:26:24,583 --> 01:26:27,916 Tuples you can do the exact same thing, but you cannot change them once 545 01:26:27,916 --> 01:26:29,083 you've created them. 546 01:26:29,083 --> 01:26:33,041 A dictionary is more like a list in that it is mutable. 547 01:26:33,041 --> 01:26:34,208 You can change it. 548 01:26:34,208 --> 01:26:37,583 But the way you index into a dictionary is 549 01:26:37,583 --> 01:26:40,541 by way of these keys, these strings, as we keep saying, 550 01:26:40,541 --> 01:26:42,250 rather than by numbers-- 551 01:26:42,250 --> 01:26:45,083 those numeric indices. 552 01:26:45,083 --> 01:26:47,875 All right, well, let me propose that there is yet 553 01:26:47,875 --> 01:26:50,458 another way of solving this problem. 554 01:26:50,458 --> 01:26:53,875 And I would argue that there's now an opportunity at hand. 555 01:26:53,875 --> 01:26:56,750 Even though this program isn't particularly complicated-- 556 01:26:56,750 --> 01:27:01,208 all I'm doing is collecting a name from the user and a house 557 01:27:01,208 --> 01:27:03,166 from the user-- you could imagine wanting, 558 01:27:03,166 --> 01:27:05,291 longer term, to collect even more information, 559 01:27:05,291 --> 01:27:07,791 like the student's patronus or magical spell 560 01:27:07,791 --> 01:27:10,666 or a whole bunch of other information that might belong in a student. 561 01:27:10,666 --> 01:27:15,291 And right now, we're just using these very general purpose data 562 01:27:15,291 --> 01:27:16,458 types in Python-- 563 01:27:16,458 --> 01:27:20,083 a tuple to combine some values together; a list to do the same, 564 01:27:20,083 --> 01:27:23,625 but let us change it later; a dictionary, which is more powerful 565 01:27:23,625 --> 01:27:25,250 because it's a little more structured. 566 01:27:25,250 --> 01:27:28,708 It does have keys, and it has values, not just values. 567 01:27:28,708 --> 01:27:30,250 But you know what? 568 01:27:30,250 --> 01:27:33,500 We wouldn't have to be having this conversation if the authors of Python 569 01:27:33,500 --> 01:27:37,125 had just given us a data type called student. 570 01:27:37,125 --> 01:27:39,000 Wouldn't it have been nice if there were just 571 01:27:39,000 --> 01:27:42,166 a type of variable I could create in my code called student? 572 01:27:42,166 --> 01:27:43,916 Then we wouldn't have to figure out, well, 573 01:27:43,916 --> 01:27:46,416 do we use a tuple or a list or a dictionary? 574 01:27:46,416 --> 01:27:48,333 But that's pretty reasonable. 575 01:27:48,333 --> 01:27:52,166 You can imagine just how slippery of a slope that is, so to speak, 576 01:27:52,166 --> 01:27:55,166 if the creators of a language had to anticipate 577 01:27:55,166 --> 01:27:58,250 all the possible types of data that programmers like you 578 01:27:58,250 --> 01:28:00,375 and me want to store in your programs. 579 01:28:00,375 --> 01:28:02,875 So they just gave us these general purpose tools. 580 01:28:02,875 --> 01:28:05,750 But they gave us another general purpose tool 581 01:28:05,750 --> 01:28:09,375 that's going to allow us to create our own data types as well 582 01:28:09,375 --> 01:28:14,291 and actually give them names, and that terminology is a class. 583 01:28:14,291 --> 01:28:20,125 A class is like a blueprint for pieces of data objects. 584 01:28:20,125 --> 01:28:24,166 A class is a mold that you can define and give a name. 585 01:28:24,166 --> 01:28:26,875 And when you use that mold or you use that blueprint, 586 01:28:26,875 --> 01:28:31,000 you get types of data that are designed exactly as you want. 587 01:28:31,000 --> 01:28:36,208 So in short, classes allow you to invent your own data types in Python 588 01:28:36,208 --> 01:28:37,375 and give them a name. 589 01:28:37,375 --> 01:28:41,291 And this is a primary feature of object oriented programming, 590 01:28:41,291 --> 01:28:44,041 to be able to create your own objects in this way 591 01:28:44,041 --> 01:28:48,291 and, in the case of Python in classes, even give them some custom names. 592 01:28:48,291 --> 01:28:50,333 So what does this mean in real terms? 593 01:28:50,333 --> 01:28:52,375 Well, let me go ahead and come back to VS Code 594 01:28:52,375 --> 01:28:56,500 here, and let me propose that we introduce a little bit of new syntax. 595 01:28:56,500 --> 01:28:58,916 I'm going to go ahead and clear my terminal window first. 596 01:28:58,916 --> 01:29:02,791 I'm going to go to the top of my file, and I'm just going to start a thought 597 01:29:02,791 --> 01:29:03,916 but not finish it yet. 598 01:29:03,916 --> 01:29:09,083 I'm going to use this new keyword for classes, called, literally, class, 599 01:29:09,083 --> 01:29:11,708 so the new keyword we're going to have here. 600 01:29:11,708 --> 01:29:13,916 And if I go back to our slides here, this 601 01:29:13,916 --> 01:29:16,083 would be the official URL where you can read up more 602 01:29:16,083 --> 01:29:18,000 on this particular feature of Python. 603 01:29:18,000 --> 01:29:22,083 In the official tutorial, class is a new keyword we can use. 604 01:29:22,083 --> 01:29:25,500 Now, this is coincidentally related to students because students take classes, 605 01:29:25,500 --> 01:29:28,500 but it has nothing to do with the fact that we're dealing with students. 606 01:29:28,500 --> 01:29:31,791 Class is a general purpose term in a lot of languages-- 607 01:29:31,791 --> 01:29:35,500 Python among them-- that allow you to define these custom containers 608 01:29:35,500 --> 01:29:38,625 with custom names for pieces of data. 609 01:29:38,625 --> 01:29:39,875 So let's go back to VS Code. 610 01:29:39,875 --> 01:29:41,333 Let's use this new keyword. 611 01:29:41,333 --> 01:29:44,500 And let me propose that we create a class called Student. 612 01:29:44,500 --> 01:29:47,000 And by convention, I'm going to use a capital S here, 613 01:29:47,000 --> 01:29:49,583 and I'm going to go ahead, and with a colon, 614 01:29:49,583 --> 01:29:52,500 get to, later, the implementation of this class. 615 01:29:52,500 --> 01:29:55,625 So I'm just going to use dot dot dot, which is a valid placeholder for now, 616 01:29:55,625 --> 01:29:57,833 that just indicates to me that I'm going to come back 617 01:29:57,833 --> 01:29:59,000 to implementing this later. 618 01:29:59,000 --> 01:30:02,125 But as of now, it does, in fact, exist. 619 01:30:02,125 --> 01:30:08,458 I now have a student class defined for me that I can now use in my code here. 620 01:30:08,458 --> 01:30:09,875 How am I going to use it? 621 01:30:09,875 --> 01:30:13,250 Well, first of all, let me go down to get_student, 622 01:30:13,250 --> 01:30:18,625 and let me change this code to no longer use a dictionary but to use this class. 623 01:30:18,625 --> 01:30:19,666 I'm going to do this. 624 01:30:19,666 --> 01:30:22,791 I'm going to give myself a variable called student, as I've done before, 625 01:30:22,791 --> 01:30:26,166 but I'm going to set it equal to capital Student (). 626 01:30:26,166 --> 01:30:28,916 627 01:30:28,916 --> 01:30:32,208 So I'm going to do what appears to be calling a function 628 01:30:32,208 --> 01:30:35,583 and that function, Student with a capital S, notice, 629 01:30:35,583 --> 01:30:39,500 matches the name that I gave this class at the top of my file. 630 01:30:39,500 --> 01:30:41,166 All right, what do I next want to do? 631 01:30:41,166 --> 01:30:43,333 I'm going to go ahead and give this student a name. 632 01:30:43,333 --> 01:30:46,041 Now, if I were still using a dictionary, I 633 01:30:46,041 --> 01:30:50,541 would say student, quote, unquote, "name," using square brackets. 634 01:30:50,541 --> 01:30:52,125 But this is not a dictionary. 635 01:30:52,125 --> 01:30:54,500 It turns out classes have what, for now, we'll 636 01:30:54,500 --> 01:30:58,916 call attributes, properties of sorts that allow you to specify values inside 637 01:30:58,916 --> 01:30:59,416 of them. 638 01:30:59,416 --> 01:31:01,708 And the syntax for that happens to be a dot. 639 01:31:01,708 --> 01:31:03,166 We've seen dots before. 640 01:31:03,166 --> 01:31:06,333 We've used it in the context of modules and libraries, more generally. 641 01:31:06,333 --> 01:31:08,875 This is another similar in spirit use of a dot 642 01:31:08,875 --> 01:31:12,125 that allows you to get at something inside of something else. 643 01:31:12,125 --> 01:31:15,208 So student.name is going to be the syntax 644 01:31:15,208 --> 01:31:18,083 I use for giving this student a name. 645 01:31:18,083 --> 01:31:21,666 And that name is going to be whatever the return value of "Name" is. 646 01:31:21,666 --> 01:31:24,958 And then I'm going to go ahead and say student.house to give 647 01:31:24,958 --> 01:31:29,291 another attribute called "House" and give that the return value of input 648 01:31:29,291 --> 01:31:30,750 here, prompting the user for house. 649 01:31:30,750 --> 01:31:33,625 And then, as before, I'm just going to return student. 650 01:31:33,625 --> 01:31:37,166 But now what's really powerful about class, 651 01:31:37,166 --> 01:31:39,791 and object-oriented programming more generally, 652 01:31:39,791 --> 01:31:43,375 is that I've created this custom data type called, literally, 653 01:31:43,375 --> 01:31:47,333 Student, capital S. I've stored one such student in a variable 654 01:31:47,333 --> 01:31:50,833 like I can always do in a variable called student, lowercase s. 655 01:31:50,833 --> 01:31:52,333 But I could call it anything I want. 656 01:31:52,333 --> 01:31:56,166 It just makes sense to call it student as well, but lowercase for clarity. 657 01:31:56,166 --> 01:31:58,291 And then I'm returning that variable. 658 01:31:58,291 --> 01:32:01,666 And because of my syntax in lines 14 and 15, 659 01:32:01,666 --> 01:32:05,083 that has the result of putting inside of that class 660 01:32:05,083 --> 01:32:09,291 a name attribute and a house attribute. 661 01:32:09,291 --> 01:32:11,625 I just need to make one more change up here. 662 01:32:11,625 --> 01:32:13,416 I'm going to go ahead and remove our Padma 663 01:32:13,416 --> 01:32:15,500 code, just so we can focus only on what's new, 664 01:32:15,500 --> 01:32:17,416 rather than fixing her house. 665 01:32:17,416 --> 01:32:20,208 And I'm going to go in here and change the syntax that 666 01:32:20,208 --> 01:32:21,916 previously was for dictionaries. 667 01:32:21,916 --> 01:32:26,958 Again, dictionaries use square brackets and then strings in quotes-- 668 01:32:26,958 --> 01:32:29,958 either single quotes or double quotes, depending on the context. 669 01:32:29,958 --> 01:32:33,208 Here, though, I'm going to change this to be student.name, 670 01:32:33,208 --> 01:32:37,458 and over here, I'm going to change it to be student.house. 671 01:32:37,458 --> 01:32:40,791 And that's just going to be my new syntax for getting the contents of what 672 01:32:40,791 --> 01:32:43,250 appears to be a class called student. 673 01:32:43,250 --> 01:32:47,458 Let me go ahead and rerun Python of student.py, Enter. 674 01:32:47,458 --> 01:32:49,333 Let's type in Harry's name as before. 675 01:32:49,333 --> 01:32:52,500 Let's put him in Gryffindor, crossing our fingers as we often do, 676 01:32:52,500 --> 01:32:56,500 and Harry is indeed from Gryffindor. 677 01:32:56,500 --> 01:32:58,166 What, though, have I done? 678 01:32:58,166 --> 01:33:01,000 Let's introduce one other bit of terminology here 679 01:33:01,000 --> 01:33:05,583 it turns out that I can create a class, using that class keyword. 680 01:33:05,583 --> 01:33:09,958 But any time you use a class, you're creating what are called objects. 681 01:33:09,958 --> 01:33:14,666 And here is the word objects, as an object-oriented programming, or OOP. 682 01:33:14,666 --> 01:33:16,250 Let me go back to my code here. 683 01:33:16,250 --> 01:33:18,916 And even though I haven't really implemented much of it at all-- 684 01:33:18,916 --> 01:33:20,958 I literally just left it with a dot, dot, dot-- 685 01:33:20,958 --> 01:33:25,416 that's enough code, lines 1 and 2, to just invent a new data type called 686 01:33:25,416 --> 01:33:29,708 Student, capital S, that may or may not have some future functionality as well. 687 01:33:29,708 --> 01:33:31,375 That's enough to create a class. 688 01:33:31,375 --> 01:33:34,166 What, though, am I doing on line 11? 689 01:33:34,166 --> 01:33:40,083 On line 11, what I'm technically doing is creating an object of that class. 690 01:33:40,083 --> 01:33:42,000 So this, too, is another term of art. 691 01:33:42,000 --> 01:33:44,791 You create objects from classes. 692 01:33:44,791 --> 01:33:47,000 So if we go back to that metaphor, that a class is 693 01:33:47,000 --> 01:33:50,916 like a blueprint for a house or a class is like a mold, 694 01:33:50,916 --> 01:33:54,750 an object is when you use that blueprint to build a specific house 695 01:33:54,750 --> 01:33:58,458 or something that comes out of-- in plaster, the mold, when you actually 696 01:33:58,458 --> 01:34:01,250 use that mold to create such an object. 697 01:34:01,250 --> 01:34:04,916 So a class is, again, the definition of a new data type. 698 01:34:04,916 --> 01:34:10,125 The object is the incarnation of, or technically instantiation of. 699 01:34:10,125 --> 01:34:13,583 And another term for objects would actually be an instance. 700 01:34:13,583 --> 01:34:16,208 You have instances of classes as well. 701 01:34:16,208 --> 01:34:17,458 So that's a lot of vocabulary. 702 01:34:17,458 --> 01:34:19,708 But at the end of the day, it just boils down to this. 703 01:34:19,708 --> 01:34:22,750 You can define your own class, which is really your own data type. 704 01:34:22,750 --> 01:34:26,791 You can then store attributes inside of it, using this dot notation here. 705 01:34:26,791 --> 01:34:31,041 And then you can access those same attributes using code like this here. 706 01:34:31,041 --> 01:34:33,958 And now, I have a proper "student" data type, 707 01:34:33,958 --> 01:34:36,625 and I don't have to hack something together 708 01:34:36,625 --> 01:34:39,000 using a tuple or a list or even a dictionary. 709 01:34:39,000 --> 01:34:43,500 I now have a proper data type called "student" that the authors of Python 710 01:34:43,500 --> 01:34:46,041 didn't give me; I gave myself. 711 01:34:46,041 --> 01:34:50,791 Any questions now on classes, this new keyword, class, or this idea 712 01:34:50,791 --> 01:34:53,625 of these objects or instances thereof? 713 01:34:53,625 --> 01:34:58,333 AUDIENCE: Is the class object mutable or immutable? 714 01:34:58,333 --> 01:34:59,666 DAVID J. MALAN: A good question. 715 01:34:59,666 --> 01:35:02,208 And we've clearly laid the stage for having that conversation 716 01:35:02,208 --> 01:35:03,541 about every data type now. 717 01:35:03,541 --> 01:35:07,958 We will see that they are mutable, but you can make them immutable. 718 01:35:07,958 --> 01:35:10,083 So you can get the best of both worlds. 719 01:35:10,083 --> 01:35:13,416 Now, by writing some actual code-- and we'll write more code than the dot, 720 01:35:13,416 --> 01:35:18,208 dot, dot in just a bit, other questions on classes or these objects thereof? 721 01:35:18,208 --> 01:35:21,625 AUDIENCE: Then what would be the properties of those classes? 722 01:35:21,625 --> 01:35:24,750 DAVID J. MALAN: So at the moment, the properties of-- or the attributes of, 723 01:35:24,750 --> 01:35:28,250 as I've been calling them thus far-- would just be "Name" and "House." 724 01:35:28,250 --> 01:35:32,333 It turns out that there may very well be other attributes built into classes 725 01:35:32,333 --> 01:35:33,541 that we may see before long. 726 01:35:33,541 --> 01:35:36,166 But for now, the only two attributes that I care about 727 01:35:36,166 --> 01:35:38,333 are the ones that I myself created-- 728 01:35:38,333 --> 01:35:42,208 namely "Name" and "House" or, again, what I would call attributes. 729 01:35:42,208 --> 01:35:43,916 And in a little bit, we're going to start 730 01:35:43,916 --> 01:35:47,541 calling those same attributes, more technically, instance variables. 731 01:35:47,541 --> 01:35:50,708 "Name" and "House," as I presented them here in VS Code 732 01:35:50,708 --> 01:35:55,750 are really just variables called "name" and called "house" inside 733 01:35:55,750 --> 01:36:00,125 of an object whose type is student. 734 01:36:00,125 --> 01:36:02,583 All right, so what more can we do with these classes? 735 01:36:02,583 --> 01:36:06,208 Well, again, on line 11 is where we're instantiating 736 01:36:06,208 --> 01:36:11,000 an object of the student class and assigning it to a student variable. 737 01:36:11,000 --> 01:36:12,416 We're then adding attributes-- 738 01:36:12,416 --> 01:36:16,000 "Name" and "House," respectively-- on lines 12 and 13 currently. 739 01:36:16,000 --> 01:36:17,958 Both of those have values that are technically 740 01:36:17,958 --> 01:36:21,208 strings or strs, because that's what the return value of the input is. 741 01:36:21,208 --> 01:36:24,000 But those attributes values could actually be any data type. 742 01:36:24,000 --> 01:36:27,000 We're just keeping things simple and focusing on defining students 743 01:36:27,000 --> 01:36:28,333 in terms of two strings-- 744 01:36:28,333 --> 01:36:29,375 "Name" and "House." 745 01:36:29,375 --> 01:36:32,125 And then, on line 14, we're returning that variable. 746 01:36:32,125 --> 01:36:35,333 We're returning that object to main so that we can actually 747 01:36:35,333 --> 01:36:37,708 print out who is from what house. 748 01:36:37,708 --> 01:36:40,958 Well, let's go ahead and add a bit more functionality here because, right now, 749 01:36:40,958 --> 01:36:44,166 on lines 12 and 13, this is a little manual. 750 01:36:44,166 --> 01:36:47,333 And it's a little reckless of me to just be putting anything 751 01:36:47,333 --> 01:36:50,333 I want inside of this student object. 752 01:36:50,333 --> 01:36:53,041 It turns out with classes, unlike with dictionaries, 753 01:36:53,041 --> 01:36:57,625 we can actually standardize, all the more, what those attributes can be 754 01:36:57,625 --> 01:37:00,291 and what kinds of values you can set them to. 755 01:37:00,291 --> 01:37:02,375 So let me go ahead and do this. 756 01:37:02,375 --> 01:37:04,375 Let me propose that it would actually be really 757 01:37:04,375 --> 01:37:08,875 nice if, instead of doing this here, let me go ahead 758 01:37:08,875 --> 01:37:10,833 and simplify my code as follows. 759 01:37:10,833 --> 01:37:13,708 Let me go ahead and give myself a local variable called name 760 01:37:13,708 --> 01:37:17,208 and set it equal to the return value of input, like we've done many times now 761 01:37:17,208 --> 01:37:17,791 already. 762 01:37:17,791 --> 01:37:20,333 Let me give myself one other variable for now, called house, 763 01:37:20,333 --> 01:37:23,333 and set it equal to the return value of input 764 01:37:23,333 --> 01:37:25,291 as well, prompting the user for their house. 765 01:37:25,291 --> 01:37:30,416 And now, instead of creating a student object from my student class 766 01:37:30,416 --> 01:37:34,500 and then manually putting the name attribute inside of it 767 01:37:34,500 --> 01:37:37,166 and the house attribute inside of it, let 768 01:37:37,166 --> 01:37:39,458 me actually do something more powerful. 769 01:37:39,458 --> 01:37:40,583 Let me do this. 770 01:37:40,583 --> 01:37:45,333 Let me call that Student function, which is identical to the class name-- 771 01:37:45,333 --> 01:37:48,125 just by defining a class, you get a function 772 01:37:48,125 --> 01:37:52,583 whose name is identical to the class name, with the capital letter included. 773 01:37:52,583 --> 01:37:54,625 But instead of just doing open parenthesis, 774 01:37:54,625 --> 01:37:58,166 closed parenthesis, let me pass in the name that I 775 01:37:58,166 --> 01:38:02,791 want to fill this object with and the house that I want to put in that object 776 01:38:02,791 --> 01:38:03,375 as well. 777 01:38:03,375 --> 01:38:09,375 And now let me set the return value as before to be student equals like this. 778 01:38:09,375 --> 01:38:11,083 So what have I done that's different? 779 01:38:11,083 --> 01:38:13,583 Fundamentally, I'm still getting user input in the same way. 780 01:38:13,583 --> 01:38:16,750 I'm using input on line 11 and input on line 12. 781 01:38:16,750 --> 01:38:21,125 And I just so happen to be storing those return values in local variables. 782 01:38:21,125 --> 01:38:24,875 And now we're setting the stage for the more powerful features of classes 783 01:38:24,875 --> 01:38:27,208 and object-oriented programming more generally. 784 01:38:27,208 --> 01:38:31,500 Notice that I'm deliberately passing to this capital S Student 785 01:38:31,500 --> 01:38:33,875 function, name, house-- 786 01:38:33,875 --> 01:38:36,291 I'm passing in arguments to the function. 787 01:38:36,291 --> 01:38:39,750 Now, the student class is not going to know what to do with those yet, 788 01:38:39,750 --> 01:38:44,791 but now I'm standardizing how I'm passing data into this student class. 789 01:38:44,791 --> 01:38:47,833 And ultimately, it's going to give me an opportunity to error 790 01:38:47,833 --> 01:38:51,166 check those inputs, to make sure that the name is valid, that it has a value 791 01:38:51,166 --> 01:38:52,875 and it's not just the user hitting Enter. 792 01:38:52,875 --> 01:38:56,041 It's going to allow me to ensure that it's a valid house, that it's 793 01:38:56,041 --> 01:38:59,208 Gryffindor or Hufflepuff or Ravenclaw or Slytherin 794 01:38:59,208 --> 01:39:02,916 or not just hitting Enter or some random value that the user types in. 795 01:39:02,916 --> 01:39:05,916 Because I'm passing "Name" and "House" to the student 796 01:39:05,916 --> 01:39:08,791 class, this particular function, I'm going 797 01:39:08,791 --> 01:39:11,416 to have more control over the correctness of my data. 798 01:39:11,416 --> 01:39:14,541 So let's now go up to the student class, which, up until now, 799 01:39:14,541 --> 01:39:16,125 I left as just dot, dot, dot. 800 01:39:16,125 --> 01:39:19,625 It turns out that, in the context of classes, 801 01:39:19,625 --> 01:39:23,916 there are a number of not just attributes or instance 802 01:39:23,916 --> 01:39:27,041 variables that you can put inside, but also methods. 803 01:39:27,041 --> 01:39:32,083 Classes come with certain methods, or functions inside of them, 804 01:39:32,083 --> 01:39:35,708 that you can define, and they just behave in a special way, 805 01:39:35,708 --> 01:39:37,875 by nature of how Python works. 806 01:39:37,875 --> 01:39:42,625 These functions allow you to determine behavior in a standard way. 807 01:39:42,625 --> 01:39:45,083 They are special methods in that sense. 808 01:39:45,083 --> 01:39:46,333 Now, what do I mean by this. 809 01:39:46,333 --> 01:39:48,083 Well, let me go back to VS Code here. 810 01:39:48,083 --> 01:39:53,000 And let me propose that I start to define a standard function 811 01:39:53,000 --> 01:39:56,916 called underscore underscore, or Dunder, as it's 812 01:39:56,916 --> 01:39:59,916 abbreviated, init, underscore underscore, 813 01:39:59,916 --> 01:40:02,916 and then I'm going to go ahead and do open parentheses, 814 01:40:02,916 --> 01:40:06,875 and then I'm going to put in here, literally, the word self. 815 01:40:06,875 --> 01:40:08,416 More on that in just a moment. 816 01:40:08,416 --> 01:40:11,791 But now, inside of this function, I'm going to have an opportunity 817 01:40:11,791 --> 01:40:16,791 to customize this class's objects. 818 01:40:16,791 --> 01:40:20,333 That is to say this underscore, underscore init method, 819 01:40:20,333 --> 01:40:25,208 or Dunder init method is specifically known as an instance method, 820 01:40:25,208 --> 01:40:26,708 and it's called exactly this. 821 01:40:26,708 --> 01:40:28,875 This is designed by the authors of Python. 822 01:40:28,875 --> 01:40:34,291 And if you want to initialize the contents of an object from a class, 823 01:40:34,291 --> 01:40:37,875 you define this method, and we'll see what it's about to do here. 824 01:40:37,875 --> 01:40:41,250 Let me go back to VS Code, and let me do something like this. 825 01:40:41,250 --> 01:40:47,750 self.name = name, and self.house = house. 826 01:40:47,750 --> 01:40:51,250 But I don't want to just init this object very generically. 827 01:40:51,250 --> 01:40:56,166 I want this method, called init, to take in not just self but name, 828 01:40:56,166 --> 01:40:58,375 house as well. 829 01:40:58,375 --> 01:40:59,916 Now, what in the world is going on? 830 01:40:59,916 --> 01:41:01,708 Because there's a lot of weird syntax here. 831 01:41:01,708 --> 01:41:03,375 There's this Dunder init method-- 832 01:41:03,375 --> 01:41:05,791 double underscore, init, double underscore. 833 01:41:05,791 --> 01:41:08,750 There's, all of a sudden, this parameter called self. 834 01:41:08,750 --> 01:41:12,458 And then there's this new syntax-- self.name and self.house. 835 01:41:12,458 --> 01:41:16,541 Now you're seeing really a manifestation of object-oriented programming. 836 01:41:16,541 --> 01:41:18,250 It's not all that different fundamentally 837 01:41:18,250 --> 01:41:20,541 from what we've been doing for weeks with dictionaries, 838 01:41:20,541 --> 01:41:23,125 by adding keys to dictionaries. 839 01:41:23,125 --> 01:41:28,333 But in this case, we're adding variables to objects, a.k.a. 840 01:41:28,333 --> 01:41:30,500 instance variables to objects. 841 01:41:30,500 --> 01:41:31,458 Now, what's going on? 842 01:41:31,458 --> 01:41:32,541 Let's do this in reverse. 843 01:41:32,541 --> 01:41:34,750 Let's go back to the line of code we wrote earlier. 844 01:41:34,750 --> 01:41:38,708 On line 15, I am treating the name of this class-- 845 01:41:38,708 --> 01:41:41,916 Student with a capital S-- as a function. 846 01:41:41,916 --> 01:41:44,583 And I am passing in two values-- 847 01:41:44,583 --> 01:41:45,916 "Name" and "House." 848 01:41:45,916 --> 01:41:48,708 What I've highlighted here on the screen, on line 15, 849 01:41:48,708 --> 01:41:51,250 is generally known as a constructor call. 850 01:41:51,250 --> 01:41:56,875 This is a line of code that is going to construct a student object for me. 851 01:41:56,875 --> 01:42:02,791 Using synonyms, it is going to instantiate a student object for me. 852 01:42:02,791 --> 01:42:05,583 And again, how is it going to create that object? 853 01:42:05,583 --> 01:42:09,458 It's going to use the student class as a template, as a mold of sorts 854 01:42:09,458 --> 01:42:12,291 so that every student is structured the same. 855 01:42:12,291 --> 01:42:13,875 Every student is going to have a name. 856 01:42:13,875 --> 01:42:15,458 Every student's going to have a house. 857 01:42:15,458 --> 01:42:20,916 But because I can pass in arguments to this Student function, capital S, 858 01:42:20,916 --> 01:42:26,666 I'm going to be able to customize the contents of that object. 859 01:42:26,666 --> 01:42:29,666 So if you think about the real world-- if you've ever been on a street 860 01:42:29,666 --> 01:42:34,666 or a neighborhood where all of the houses look the same but they might be 861 01:42:34,666 --> 01:42:37,541 painted differently; they might be decorated a little bit differently 862 01:42:37,541 --> 01:42:40,958 on the outside, all of those houses might have been built using the exact 863 01:42:40,958 --> 01:42:42,541 same blueprint-- 864 01:42:42,541 --> 01:42:43,875 a mold, if you will. 865 01:42:43,875 --> 01:42:48,083 But then you can specialize exactly the finer points of those houses. 866 01:42:48,083 --> 01:42:51,000 By painting the outside a different color or planting different trees, 867 01:42:51,000 --> 01:42:52,500 you can style them differently. 868 01:42:52,500 --> 01:42:57,750 Similar in spirit here, we have a Student blueprint 869 01:42:57,750 --> 01:43:01,583 that's always going to have now a name and a house, but it's up to you and me 870 01:43:01,583 --> 01:43:04,166 to pass in any name and any house that we want. 871 01:43:04,166 --> 01:43:06,041 Now, where is this function? 872 01:43:06,041 --> 01:43:09,125 The fact that I'm calling Student, capital, S and then a parenthesis 873 01:43:09,125 --> 01:43:11,250 and a closed parenthesis with arguments inside 874 01:43:11,250 --> 01:43:12,958 suggest that there's a function somewhere 875 01:43:12,958 --> 01:43:17,500 in the world that has been defined, with def, that's going to be called. 876 01:43:17,500 --> 01:43:20,416 Well, as you might have guessed by now, the function 877 01:43:20,416 --> 01:43:25,500 that will always be called, by definition of how Python classes work, 878 01:43:25,500 --> 01:43:30,000 is a function called double underscore, init, double underscore. 879 01:43:30,000 --> 01:43:30,500 Why? 880 01:43:30,500 --> 01:43:33,333 It's a crazy name, but it's what the authors of Python 881 01:43:33,333 --> 01:43:38,750 chose to just implement the initialization of an object in Python. 882 01:43:38,750 --> 01:43:40,916 Now, the only weird thing-- 883 01:43:40,916 --> 01:43:43,125 especially weird thing, I will admit, is this. 884 01:43:43,125 --> 01:43:49,125 It would be way clearer, to me, too, if the only two parameters for init we're 885 01:43:49,125 --> 01:43:50,666 just name, house. 886 01:43:50,666 --> 01:43:53,541 That's how we've defined every function thus far in the class. 887 01:43:53,541 --> 01:43:57,291 You just specify the parameters that you want the function to accept. 888 01:43:57,291 --> 01:44:00,333 And that lines up with what I'm doing on line 15. 889 01:44:00,333 --> 01:44:04,541 I am only passing in two things to the student function. 890 01:44:04,541 --> 01:44:07,833 But it turns out that the authors of Python 891 01:44:07,833 --> 01:44:09,541 need to give us a little bit of help here 892 01:44:09,541 --> 01:44:14,458 because suppose that you pass in "Name" and "House" to this init method. 893 01:44:14,458 --> 01:44:17,833 And a method is just a function inside of a class. 894 01:44:17,833 --> 01:44:20,291 What are you going to do with the name and the house? 895 01:44:20,291 --> 01:44:22,333 Literally, where are you going to put them? 896 01:44:22,333 --> 01:44:26,500 If you want to remember the name and the house for this student, 897 01:44:26,500 --> 01:44:29,166 you've got to be able to store those values somewhere. 898 01:44:29,166 --> 01:44:32,416 And how do you store them in the current object 899 01:44:32,416 --> 01:44:34,833 that has just been "instantiated?" 900 01:44:34,833 --> 01:44:37,625 Well, the authors of Python decided that the convention 901 01:44:37,625 --> 01:44:41,958 is going to be that this init method also, semi secretly, takes 902 01:44:41,958 --> 01:44:44,625 a third argument, that has to come first. 903 01:44:44,625 --> 01:44:46,666 By convention, it's called self, but you could 904 01:44:46,666 --> 01:44:48,250 call it technically anything you want. 905 01:44:48,250 --> 01:44:50,208 But the convention is to always call it self. 906 01:44:50,208 --> 01:44:53,500 And self, as its name implies, gives you access 907 01:44:53,500 --> 01:44:56,791 to the current object that was just created. 908 01:44:56,791 --> 01:44:58,000 What does that mean? 909 01:44:58,000 --> 01:45:02,166 Again, now, on line 14, now that it's moved down a little bit, 910 01:45:02,166 --> 01:45:04,083 this line here is a constructor. 911 01:45:04,083 --> 01:45:06,166 It constructs a student object. 912 01:45:06,166 --> 01:45:08,291 But there's nothing in that object initially. 913 01:45:08,291 --> 01:45:09,750 There's no name; there's no house. 914 01:45:09,750 --> 01:45:12,791 But the object exists in the computer's memory. 915 01:45:12,791 --> 01:45:16,541 It's up to, now, you to store the name and the house inside of that object. 916 01:45:16,541 --> 01:45:17,500 How do you do that? 917 01:45:17,500 --> 01:45:21,500 Well, Python will just automatically call this init method for you, 918 01:45:21,500 --> 01:45:27,500 and it's going to automatically pass in a reference to an argument that 919 01:45:27,500 --> 01:45:32,375 represents the current object that it just constructed in memory for you, 920 01:45:32,375 --> 01:45:34,708 and it's up to you to populate it with values. 921 01:45:34,708 --> 01:45:37,666 And what this means is that, inside of your init method, 922 01:45:37,666 --> 01:45:42,875 you can literally do self.name to create a new attribute, a.k.a. 923 01:45:42,875 --> 01:45:46,166 an instance variable, inside of that otherwise empty 924 01:45:46,166 --> 01:45:48,416 object and put this name inside of it. 925 01:45:48,416 --> 01:45:51,875 It allows you to do self.house and store that value of house. 926 01:45:51,875 --> 01:45:54,000 Now, you could call these things anything you want. 927 01:45:54,000 --> 01:45:54,750 They could be n. 928 01:45:54,750 --> 01:45:56,416 They could be h, as before. 929 01:45:56,416 --> 01:46:00,250 But that's really not very self-explanatory. 930 01:46:00,250 --> 01:46:04,250 Much better to do this kind of convention. self.name equals name. 931 01:46:04,250 --> 01:46:06,333 self.house equals house. 932 01:46:06,333 --> 01:46:11,708 And this is like installing into the otherwise empty object the value name 933 01:46:11,708 --> 01:46:16,083 and house and storing them in, really, identically named instance 934 01:46:16,083 --> 01:46:17,708 variables in the object. 935 01:46:17,708 --> 01:46:20,833 And again, an object is just an instance of a class. 936 01:46:20,833 --> 01:46:23,208 Now, I know that was a lot of vocabulary. 937 01:46:23,208 --> 01:46:24,875 That's a lot of weird syntax. 938 01:46:24,875 --> 01:46:28,625 So any questions on this init method, whose purpose in life, 939 01:46:28,625 --> 01:46:33,541 again, is to initialize an otherwise empty object when you first create it? 940 01:46:33,541 --> 01:46:36,791 AUDIENCE: So what is the difference between the init method and default 941 01:46:36,791 --> 01:46:37,500 constructor? 942 01:46:37,500 --> 01:46:38,833 DAVID J. MALAN: A good question. 943 01:46:38,833 --> 01:46:41,583 So in other languages-- if you programmed before. 944 01:46:41,583 --> 01:46:45,166 For instance, Java-- there are functions that are explicitly called 945 01:46:45,166 --> 01:46:47,916 constructors that construct an object. 946 01:46:47,916 --> 01:46:49,416 They initialize it with values. 947 01:46:49,416 --> 01:46:53,291 Python technically calls this init method the initialization method. 948 01:46:53,291 --> 01:46:54,958 It initializes the value. 949 01:46:54,958 --> 01:47:00,875 It's on line 15 now of my code, if I scroll back down, that I'm technically 950 01:47:00,875 --> 01:47:02,416 constructing the object. 951 01:47:02,416 --> 01:47:05,333 It turns out there's another special method in Python, 952 01:47:05,333 --> 01:47:07,291 that we won't talk about in detail today, 953 01:47:07,291 --> 01:47:11,125 called underscore underscore, new, underscore underscore 954 01:47:11,125 --> 01:47:13,375 that actually handles the process of creating 955 01:47:13,375 --> 01:47:15,458 an empty object in memory for us. 956 01:47:15,458 --> 01:47:17,833 But, generally speaking, you, the programmer, 957 01:47:17,833 --> 01:47:19,916 don't need to manipulate the new function. 958 01:47:19,916 --> 01:47:20,916 It just works for you. 959 01:47:20,916 --> 01:47:23,750 Instead, you define your own init method here 960 01:47:23,750 --> 01:47:27,041 and init function inside of your class, and that method 961 01:47:27,041 --> 01:47:29,916 initializes the contents of the object. 962 01:47:29,916 --> 01:47:32,750 So there's technically a distinction between constructing the object 963 01:47:32,750 --> 01:47:35,250 with new and initializing it with init. 964 01:47:35,250 --> 01:47:39,250 But in the world of Python, you pretty much only worry about the init method. 965 01:47:39,250 --> 01:47:42,250 Python generally does the other part for you. 966 01:47:42,250 --> 01:47:43,000 A good question. 967 01:47:43,000 --> 01:47:44,333 Others? 968 01:47:44,333 --> 01:47:48,166 AUDIENCE: What about if you want to store more than one name or more 969 01:47:48,166 --> 01:47:49,083 than one house? 970 01:47:49,083 --> 01:47:49,750 DAVID J. MALAN: A good question. 971 01:47:49,750 --> 01:47:52,666 If you want to store more than one name or more than one house, 972 01:47:52,666 --> 01:47:54,083 you can do this in different ways. 973 01:47:54,083 --> 01:47:57,916 You could create other attributes-- technically called instance variables-- 974 01:47:57,916 --> 01:48:01,708 like self.name1, self.name2. 975 01:48:01,708 --> 01:48:03,708 But we've seen, in the past, that that is not 976 01:48:03,708 --> 01:48:06,291 a very good design, just to have multiple variables to store 977 01:48:06,291 --> 01:48:07,250 multiple things. 978 01:48:07,250 --> 01:48:12,625 Maybe, instead, you have an instance variable called self.names, plural, 979 01:48:12,625 --> 01:48:15,958 and you set it equal to a list of names or a list of houses. 980 01:48:15,958 --> 01:48:19,000 Now, in this case, I don't think that really solves a problem because I'm 981 01:48:19,000 --> 01:48:22,958 trying to implement a student, singular, so it doesn't really make sense 982 01:48:22,958 --> 01:48:24,250 to have multiple first names. 983 01:48:24,250 --> 01:48:27,166 Maybe a nickname, maybe a last name, so we could add those, too. 984 01:48:27,166 --> 01:48:29,375 But I don't think we need multiple names per se 985 01:48:29,375 --> 01:48:31,333 and, in this case, multiple houses. 986 01:48:31,333 --> 01:48:34,500 But absolutely, you could do that using some of our familiar building blocks 987 01:48:34,500 --> 01:48:35,916 like lists. 988 01:48:35,916 --> 01:48:37,541 Other questions? 989 01:48:37,541 --> 01:48:40,291 AUDIENCE: How are classes or objects represented in memory? 990 01:48:40,291 --> 01:48:43,041 DAVID J. MALAN: How are classes and objects represented in memory? 991 01:48:43,041 --> 01:48:44,958 So the class is technically just code. 992 01:48:44,958 --> 01:48:48,333 It is the code on the top of my file-- lines 1 through fou4-- 993 01:48:48,333 --> 01:48:52,250 that defines that blueprint, that template, if you will. 994 01:48:52,250 --> 01:48:54,833 Objects are stored in the computer's memory 995 01:48:54,833 --> 01:48:56,333 by taking up some number of bytes. 996 01:48:56,333 --> 01:48:59,125 So you're probably familiar with bytes or kilobytes or megabytes. 997 01:48:59,125 --> 01:49:01,750 There's some chunk of bytes, probably all 998 01:49:01,750 --> 01:49:03,708 in the same location in the computer's memory 999 01:49:03,708 --> 01:49:07,916 or RAM, where those objects are stored. 1000 01:49:07,916 --> 01:49:11,500 But that's what Python, the program, handles for you. 1001 01:49:11,500 --> 01:49:14,958 Python the interpreter figures out where in the computer's memory to put it. 1002 01:49:14,958 --> 01:49:18,041 You and I, the programmers, get to think and solve problems at this level. 1003 01:49:18,041 --> 01:49:21,916 Python, the interpreter, handles those lower level details for you. 1004 01:49:21,916 --> 01:49:25,166 How about one final question on classes and objects? 1005 01:49:25,166 --> 01:49:28,125 AUDIENCE: So my question is if we can the same do 1006 01:49:28,125 --> 01:49:31,625 the same thing with the dictionaries, so why to use classes? 1007 01:49:31,625 --> 01:49:32,875 DAVID J. MALAN: Good question. 1008 01:49:32,875 --> 01:49:34,750 If you can do the same things as you can with dictionaries, 1009 01:49:34,750 --> 01:49:35,958 why should you use classes? 1010 01:49:35,958 --> 01:49:38,416 Because we are just scratching the surface now of what 1011 01:49:38,416 --> 01:49:39,833 you can do with classes. 1012 01:49:39,833 --> 01:49:42,666 Allow me to go back, now, to my keyboard and show you 1013 01:49:42,666 --> 01:49:44,375 more of what you can do with classes. 1014 01:49:44,375 --> 01:49:47,750 But in short, you can do much more with classes. 1015 01:49:47,750 --> 01:49:51,791 You can ensure the correctness of your data much more with classes. 1016 01:49:51,791 --> 01:49:53,166 You can error-check things. 1017 01:49:53,166 --> 01:49:57,375 And generally, you can design more complicated software more effectively. 1018 01:49:57,375 --> 01:49:59,458 And we'll continue to see, today, features 1019 01:49:59,458 --> 01:50:02,416 of Python and object-oriented programming more generally 1020 01:50:02,416 --> 01:50:04,875 that allows us to do just that. 1021 01:50:04,875 --> 01:50:09,333 So let me propose, in fact, that first, let's just tighten up this current 1022 01:50:09,333 --> 01:50:13,916 implementation, which again has us with an init method that just declares two 1023 01:50:13,916 --> 01:50:16,875 instance variables-- self.name and self.house, house, which, again, 1024 01:50:16,875 --> 01:50:21,416 just creates those variables inside of the otherwise empty object and assigns 1025 01:50:21,416 --> 01:50:22,125 them values-- 1026 01:50:22,125 --> 01:50:23,708 name and house, respectively. 1027 01:50:23,708 --> 01:50:26,000 Let me go ahead and just do one little thing here. 1028 01:50:26,000 --> 01:50:27,791 I don't really need this student variable. 1029 01:50:27,791 --> 01:50:31,750 Let me just tighten this up so that each time we improve or change the code, 1030 01:50:31,750 --> 01:50:35,000 we're focusing, really, on just the minimal changes alone. 1031 01:50:35,000 --> 01:50:37,250 So I've not fundamentally done anything different. 1032 01:50:37,250 --> 01:50:39,291 I just got rid of the variable name, and I'm just 1033 01:50:39,291 --> 01:50:43,125 returning the return value of this student function 1034 01:50:43,125 --> 01:50:45,125 that's constructing my new object for me. 1035 01:50:45,125 --> 01:50:48,833 So I'm just tightening things up as we've done many times in the past. 1036 01:50:48,833 --> 01:50:53,291 Well, what if something goes wrong in creating this student? 1037 01:50:53,291 --> 01:50:56,791 For instance, what if the user does not give us a name, and they just hit 1038 01:50:56,791 --> 01:50:58,791 Enter when prompted for name. 1039 01:50:58,791 --> 01:51:01,250 I don't want to put in my computer's memory 1040 01:51:01,250 --> 01:51:04,375 a bogus student object that has no name. 1041 01:51:04,375 --> 01:51:07,833 I'd ideally like to check for errors before I even create it 1042 01:51:07,833 --> 01:51:09,583 so I don't create a nameless student. 1043 01:51:09,583 --> 01:51:13,791 It would just be weird and probably a bug to have an object that has no name. 1044 01:51:13,791 --> 01:51:16,208 Similarly, I don't want the user to be able to type 1045 01:51:16,208 --> 01:51:18,708 in something random as their house. 1046 01:51:18,708 --> 01:51:21,375 At least in the world of Harry Potter, there's really 1047 01:51:21,375 --> 01:51:23,791 only four houses, at Hogwarts at least. 1048 01:51:23,791 --> 01:51:25,666 There's, again, Gryffindor and Hufflepuff 1049 01:51:25,666 --> 01:51:29,625 and Ravenclaw and Slytherin-- a list of four valid houses. 1050 01:51:29,625 --> 01:51:34,291 It would be nice if I somehow validated that the user's input is indeed 1051 01:51:34,291 --> 01:51:35,541 in that list. 1052 01:51:35,541 --> 01:51:39,916 Now, I could do all of that validation in my get_student function. 1053 01:51:39,916 --> 01:51:41,750 I could check, is the name empty? 1054 01:51:41,750 --> 01:51:44,416 If so, don't create the student object. 1055 01:51:44,416 --> 01:51:46,666 Is the house one of those four houses? 1056 01:51:46,666 --> 01:51:49,083 If not, don't create the student object. 1057 01:51:49,083 --> 01:51:52,750 But that would be rather decoupled from the student itself. 1058 01:51:52,750 --> 01:51:57,625 get_student currently exists as just my own function in my student.py file. 1059 01:51:57,625 --> 01:52:01,125 But classes-- and really, object-oriented programming-- more 1060 01:52:01,125 --> 01:52:05,291 generally encourages you to encapsulate, inside 1061 01:52:05,291 --> 01:52:09,750 of a class, all functionality related to that class. 1062 01:52:09,750 --> 01:52:12,375 So if you want to validate that a name exists-- 1063 01:52:12,375 --> 01:52:14,750 if you want to validate that a house is correct, 1064 01:52:14,750 --> 01:52:20,041 that belongs just fundamentally in the class called student itself, 1065 01:52:20,041 --> 01:52:22,583 not in some random function that you wrote elsewhere. 1066 01:52:22,583 --> 01:52:24,875 Again, this is just methodology because, again, 1067 01:52:24,875 --> 01:52:28,541 if we think about writing code that gets longer and longer, more and more 1068 01:52:28,541 --> 01:52:31,250 complicated, it should make just intuitive sense that, 1069 01:52:31,250 --> 01:52:34,250 if you keep all the house-- 1070 01:52:34,250 --> 01:52:37,208 all of the name and all of the house-related code in the student, 1071 01:52:37,208 --> 01:52:38,708 it's just better organization. 1072 01:52:38,708 --> 01:52:41,583 Keep all of the related code together, and that's probably 1073 01:52:41,583 --> 01:52:43,125 going to set you up for more success. 1074 01:52:43,125 --> 01:52:45,166 And indeed, that's part of this methodology 1075 01:52:45,166 --> 01:52:47,125 of object-oriented programming. 1076 01:52:47,125 --> 01:52:51,916 Let me go ahead now and change my students classes 1077 01:52:51,916 --> 01:52:53,875 init method to do this. 1078 01:52:53,875 --> 01:52:56,375 If the name is blank-- 1079 01:52:56,375 --> 01:52:59,333 so if not name-- and we've seen this kind of syntax before. 1080 01:52:59,333 --> 01:53:02,666 If you say in Python, Pythonically, if not name, 1081 01:53:02,666 --> 01:53:04,500 that's doing something like this. 1082 01:53:04,500 --> 01:53:07,291 If name equals, equals, quote, unquote-- 1083 01:53:07,291 --> 01:53:09,041 but I can do this a little more elegantly. 1084 01:53:09,041 --> 01:53:11,958 Just say, if not name, would be the more Pythonic. 1085 01:53:11,958 --> 01:53:15,583 Well, I want to return an error. 1086 01:53:15,583 --> 01:53:17,208 I might want to do something like this. 1087 01:53:17,208 --> 01:53:18,916 Print missing name. 1088 01:53:18,916 --> 01:53:20,541 But this is not good enough. 1089 01:53:20,541 --> 01:53:24,000 It does not suffice to just print out missing name 1090 01:53:24,000 --> 01:53:26,083 and then let the rest of the code go through. 1091 01:53:26,083 --> 01:53:27,791 All right, well, what could I do instead? 1092 01:53:27,791 --> 01:53:30,791 In the past, we've seen another technique I could do sys.exit, 1093 01:53:30,791 --> 01:53:33,583 and I could say something like missing name, and I could go up here 1094 01:53:33,583 --> 01:53:34,750 and I could import sys. 1095 01:53:34,750 --> 01:53:37,958 But this is a really obnoxious solution to the problem. 1096 01:53:37,958 --> 01:53:41,250 Just because you or maybe a colleague messed up 1097 01:53:41,250 --> 01:53:43,750 and called a function with an invalid name, 1098 01:53:43,750 --> 01:53:45,666 you're going to quit my whole program? 1099 01:53:45,666 --> 01:53:49,750 That's really, really extreme of a response, 1100 01:53:49,750 --> 01:53:52,125 and you probably don't want to do that if your program is 1101 01:53:52,125 --> 01:53:53,041 in the middle of running. 1102 01:53:53,041 --> 01:53:54,625 You might want to clean some stuff up. 1103 01:53:54,625 --> 01:53:57,958 You might want to save files you don't want to just exit a program sometimes 1104 01:53:57,958 --> 01:54:01,208 in some arbitrary line, just because input was invalid. 1105 01:54:01,208 --> 01:54:03,333 So I don't think we want to do that either. 1106 01:54:03,333 --> 01:54:07,375 But we do, now, have a mechanism for signaling errors. 1107 01:54:07,375 --> 01:54:09,500 Unfortunately, I can't do something like this. 1108 01:54:09,500 --> 01:54:13,625 I could try returning none and say, uh-uh, this student does not exist. 1109 01:54:13,625 --> 01:54:15,666 I'm going to hand you back none instead. 1110 01:54:15,666 --> 01:54:16,916 But it's too late. 1111 01:54:16,916 --> 01:54:21,416 If we scroll back down to where I'm creating the student, it's on line 17 1112 01:54:21,416 --> 01:54:23,083 now where I've highlighted this code. 1113 01:54:23,083 --> 01:54:25,541 The student has already been created. 1114 01:54:25,541 --> 01:54:28,750 There is an object somewhere in the computer's memory 1115 01:54:28,750 --> 01:54:30,375 that's structured as a student. 1116 01:54:30,375 --> 01:54:32,708 It just doesn't have any values inside of it. 1117 01:54:32,708 --> 01:54:35,541 But it's too late, therefore, to return none. 1118 01:54:35,541 --> 01:54:36,666 That ship has sailed. 1119 01:54:36,666 --> 01:54:37,958 The object exists. 1120 01:54:37,958 --> 01:54:40,458 You can't just suddenly say, nope, nope, there is no object. 1121 01:54:40,458 --> 01:54:41,625 There is an object. 1122 01:54:41,625 --> 01:54:43,583 It's up to you to signal an error. 1123 01:54:43,583 --> 01:54:45,333 And how do you signal an error? 1124 01:54:45,333 --> 01:54:48,083 Well, we've actually seen this before, but we haven't had occasion 1125 01:54:48,083 --> 01:54:49,583 to create our own errors. 1126 01:54:49,583 --> 01:54:54,750 It turns out, in Python, there's another keyword related to exceptions 1127 01:54:54,750 --> 01:54:58,583 that Python itself uses to raise all of those exceptions we've 1128 01:54:58,583 --> 01:54:59,750 talked about in the past. 1129 01:54:59,750 --> 01:55:04,500 When you've caught things like value errors or other such exceptions that 1130 01:55:04,500 --> 01:55:09,083 come with Python, well, it turns out you, the programmer can raise-- 1131 01:55:09,083 --> 01:55:12,875 that is create your own exceptions when something just really goes wrong-- 1132 01:55:12,875 --> 01:55:15,750 not wrong enough that you want to quit and exit the whole program, 1133 01:55:15,750 --> 01:55:18,958 but enough that you need to somehow alert the programmer 1134 01:55:18,958 --> 01:55:20,583 that there has been an error. 1135 01:55:20,583 --> 01:55:23,583 Something exceptional, in a very bad way-- something 1136 01:55:23,583 --> 01:55:29,250 exceptional has happened, and let them try to catch that exception as needed. 1137 01:55:29,250 --> 01:55:32,250 So let me go back to VS Code here and propose 1138 01:55:32,250 --> 01:55:36,500 that, if the user passes in an invalid name-- it's just empty, 1139 01:55:36,500 --> 01:55:37,833 so there's not a name. 1140 01:55:37,833 --> 01:55:39,875 Well, what I really want to do is this. 1141 01:55:39,875 --> 01:55:43,041 I want to raise a value error. 1142 01:55:43,041 --> 01:55:45,208 And we've seen the value errors before. 1143 01:55:45,208 --> 01:55:47,833 We've created value errors accidentally before. 1144 01:55:47,833 --> 01:55:51,416 And generally, you and I have tried to catch them if they happen. 1145 01:55:51,416 --> 01:55:55,375 Well, the flip side of this feature of exceptions in a language like Python 1146 01:55:55,375 --> 01:55:58,125 is that you, the programmer, can also raise exceptions 1147 01:55:58,125 --> 01:56:00,000 when something exceptional happens. 1148 01:56:00,000 --> 01:56:01,791 And you can even be more precise. 1149 01:56:01,791 --> 01:56:05,250 You don't have to raise a generic value error and let the programmer figure out 1150 01:56:05,250 --> 01:56:06,083 what went wrong. 1151 01:56:06,083 --> 01:56:10,291 You can treat value error and all exceptions in Python like functions 1152 01:56:10,291 --> 01:56:14,583 and actually pass to them an explanatory message like, quote, unquote, 1153 01:56:14,583 --> 01:56:17,875 "Missing name," so that at least the programmer, when they encounter 1154 01:56:17,875 --> 01:56:19,458 this error, knows, oh, I messed up. 1155 01:56:19,458 --> 01:56:22,833 I didn't make sure that the user has a name. 1156 01:56:22,833 --> 01:56:25,125 And now, what do you want to do instead? 1157 01:56:25,125 --> 01:56:28,708 Well, now, if you're the programmer, you could do something like this. 1158 01:56:28,708 --> 01:56:34,708 You could try to create a student except if there's a value error. 1159 01:56:34,708 --> 01:56:37,208 Then you could handle it in some way. 1160 01:56:37,208 --> 01:56:39,500 And I'm going to wave my hand with a dot, dot, dot, 1161 01:56:39,500 --> 01:56:40,791 at how you would handle it. 1162 01:56:40,791 --> 01:56:44,666 But you would handle it using try and accept, just like we have in the past, 1163 01:56:44,666 --> 01:56:46,666 and that would allow you, the programmer, 1164 01:56:46,666 --> 01:56:48,166 to try to create the student. 1165 01:56:48,166 --> 01:56:52,291 But if something goes wrong, OK, I'll handle it nonetheless. 1166 01:56:52,291 --> 01:56:54,958 So what's new here, again, is this raise keyword, 1167 01:56:54,958 --> 01:56:59,250 that just lets you and I actually raise our own exceptions 1168 01:56:59,250 --> 01:57:00,458 to signal these errors. 1169 01:57:00,458 --> 01:57:03,000 Well, let me go back to my code here, and I'm just 1170 01:57:03,000 --> 01:57:06,208 going to go ahead and not bother trying or catching this error. 1171 01:57:06,208 --> 01:57:09,083 For now, we'll just focus on raising it and assume 1172 01:57:09,083 --> 01:57:12,375 that, from our recon exceptions, you could add try and accept 1173 01:57:12,375 --> 01:57:13,833 as needed in places. 1174 01:57:13,833 --> 01:57:16,916 Let me go back to the code here and propose that something else could 1175 01:57:16,916 --> 01:57:18,416 go wrong with house. 1176 01:57:18,416 --> 01:57:19,791 If there is a name, we're good. 1177 01:57:19,791 --> 01:57:22,083 But if we're given a house but it's invalid, 1178 01:57:22,083 --> 01:57:24,958 we should probably raise an exception for that, too. 1179 01:57:24,958 --> 01:57:26,208 So what if we do this? 1180 01:57:26,208 --> 01:57:32,208 If house is not in the list containing "Gryffindor," quote, unquote, 1181 01:57:32,208 --> 01:57:34,708 "Hufflepuff," quote, unquote-- 1182 01:57:34,708 --> 01:57:38,875 let's see, "Ravenclaw," quote, unquote, or "Slytherin," 1183 01:57:38,875 --> 01:57:41,833 quote, unquote, then, with my colon, let's 1184 01:57:41,833 --> 01:57:43,416 raise another type of value error. 1185 01:57:43,416 --> 01:57:45,458 But rather than raise a generic value error, 1186 01:57:45,458 --> 01:57:49,750 let's pass in an argument, quote, unquote, "Invalid house." 1187 01:57:49,750 --> 01:57:52,708 And so here we now see a capability that we 1188 01:57:52,708 --> 01:57:55,916 can do with classes that we can't with dictionaries. 1189 01:57:55,916 --> 01:58:00,541 If you add an attribute to a dictionary, a key to a dictionary, 1190 01:58:00,541 --> 01:58:02,083 it's going in no matter what. 1191 01:58:02,083 --> 01:58:06,000 Even if the name is empty, even if the house is a completely random string 1192 01:58:06,000 --> 01:58:07,833 of text that's not one of these four houses, 1193 01:58:07,833 --> 01:58:09,333 it's going into that dictionary. 1194 01:58:09,333 --> 01:58:12,708 But with a class, and by way of this init method, 1195 01:58:12,708 --> 01:58:17,750 you and I can now control exactly what's going to be installed, if you will, 1196 01:58:17,750 --> 01:58:19,041 inside of this object. 1197 01:58:19,041 --> 01:58:22,458 You have a little more control now over correctness. 1198 01:58:22,458 --> 01:58:26,583 And so now let me go ahead and scroll back down to my terminal window 1199 01:58:26,583 --> 01:58:27,291 and clear it. 1200 01:58:27,291 --> 01:58:29,500 Let me run Python of student.py. 1201 01:58:29,500 --> 01:58:31,250 Let me type in something like Harry. 1202 01:58:31,250 --> 01:58:33,958 Let me type in Gryffindor, Enter, and we see 1203 01:58:33,958 --> 01:58:35,666 that, indeed, Harry is from Gryffindor. 1204 01:58:35,666 --> 01:58:37,666 What if I made a mistake, though? 1205 01:58:37,666 --> 01:58:40,750 What if I ran Python of student.py and typed Harry as the name, 1206 01:58:40,750 --> 01:58:44,416 but this time typed in Number Four, Privet Drive, which 1207 01:58:44,416 --> 01:58:47,500 is where he grew up, instead of his proper Hogwarts house. 1208 01:58:47,500 --> 01:58:51,333 Let me hit Enter now, and now you see a value error. 1209 01:58:51,333 --> 01:58:54,458 But this isn't one that Python generated for us, per se. 1210 01:58:54,458 --> 01:58:56,166 I raised this error. 1211 01:58:56,166 --> 01:58:59,666 And therefore, if I went in and wrote more code in my get_student function, 1212 01:58:59,666 --> 01:59:04,666 I could also catch this error with our usual try except syntax. 1213 01:59:04,666 --> 01:59:09,250 So all we have now is not just classes in our toolkit, but even more powers 1214 01:59:09,250 --> 01:59:12,583 when it comes to exceptions, and not just catching them ourselves 1215 01:59:12,583 --> 01:59:15,333 but raising them ourselves, too. 1216 01:59:15,333 --> 01:59:21,708 Any questions now on this use of classes and init and now this ability 1217 01:59:21,708 --> 01:59:26,000 to raise exceptions when something goes wrong inside of the initialization? 1218 01:59:26,000 --> 01:59:29,416 AUDIENCE: So what if the user has a middle name-- 1219 01:59:29,416 --> 01:59:31,291 name, middle name, and last name? 1220 01:59:31,291 --> 01:59:32,750 How would you fix that? 1221 01:59:32,750 --> 01:59:35,250 DAVID J. MALAN: Good question. 1222 01:59:35,250 --> 01:59:38,916 If you wanted the student to have a first name, middle name, and last name, 1223 01:59:38,916 --> 01:59:41,041 we could do this in a bunch of different ways. 1224 01:59:41,041 --> 01:59:44,458 The simplest, though, if-- let me clear my screen here, and let 1225 01:59:44,458 --> 01:59:46,041 me just temporarily do this. 1226 01:59:46,041 --> 01:59:51,333 Let me propose that the init method take in a first argument, a middle argument, 1227 01:59:51,333 --> 01:59:53,000 and a last argument. 1228 01:59:53,000 --> 01:59:57,833 And then what I think I would do down here is ultimately have first = first, 1229 01:59:57,833 --> 02:00:00,916 and then I would do the same thing for middle and last. 1230 02:00:00,916 --> 02:00:05,541 So middle and middle, and then last and last. 1231 02:00:05,541 --> 02:00:08,583 And then what I would have to do here is, 1232 02:00:08,583 --> 02:00:11,208 when I actually ask the user for their name, 1233 02:00:11,208 --> 02:00:12,750 I might need to really go all out. 1234 02:00:12,750 --> 02:00:15,166 I might need to ask them first for their first name 1235 02:00:15,166 --> 02:00:18,916 and store that in a variable called first, and therefore pass in first. 1236 02:00:18,916 --> 02:00:21,833 I might similarly need to ask them for their middle name 1237 02:00:21,833 --> 02:00:25,500 and store that in a variable and then pass in a second argument, middle. 1238 02:00:25,500 --> 02:00:28,375 And then lastly, if you will, let me go ahead and create 1239 02:00:28,375 --> 02:00:31,916 a third variable called last, get the input for their last name, 1240 02:00:31,916 --> 02:00:34,041 and pass that in as well. 1241 02:00:34,041 --> 02:00:38,125 I could instead just use one input and just ask them for their whole name. 1242 02:00:38,125 --> 02:00:42,583 So type in David Malan, Enter, or David J. Malan-- all three of them, 1243 02:00:42,583 --> 02:00:45,666 and maybe I could use Python's split function, 1244 02:00:45,666 --> 02:00:47,583 maybe a regular expression to tease it apart. 1245 02:00:47,583 --> 02:00:49,625 That's probably going to be messy because there's 1246 02:00:49,625 --> 02:00:52,083 going to be people who don't have just two or three names. 1247 02:00:52,083 --> 02:00:53,458 They might have four or five. 1248 02:00:53,458 --> 02:00:55,958 So maybe sometimes it's better to have multiple prompts. 1249 02:00:55,958 --> 02:00:58,291 But that's not a problem because, with a class, 1250 02:00:58,291 --> 02:01:02,291 we have the expressiveness to take in more arguments if we want. 1251 02:01:02,291 --> 02:01:04,041 We could even take a list if we wanted. 1252 02:01:04,041 --> 02:01:06,916 But I think we'd probably want to have even more error checking then, 1253 02:01:06,916 --> 02:01:11,458 not just for name but for first, and then maybe for middle, and then 1254 02:01:11,458 --> 02:01:12,208 maybe for last. 1255 02:01:12,208 --> 02:01:14,541 So it just is more and more code, though there would be 1256 02:01:14,541 --> 02:01:17,041 ways to perhaps consolidate that, too. 1257 02:01:17,041 --> 02:01:22,500 Let me undo all of that and see if there are other questions now on classes. 1258 02:01:22,500 --> 02:01:24,291 AUDIENCE: I assume classes are something I 1259 02:01:24,291 --> 02:01:25,916 might do at the beginning of a project. 1260 02:01:25,916 --> 02:01:28,583 Can I just put them in a different file and import them 1261 02:01:28,583 --> 02:01:32,083 into my project, or my main code as needed? 1262 02:01:32,083 --> 02:01:33,208 DAVID J. MALAN: Absolutely. 1263 02:01:33,208 --> 02:01:34,208 A really good question. 1264 02:01:34,208 --> 02:01:36,083 You could imagine wanting to use this student 1265 02:01:36,083 --> 02:01:40,666 class, not just in student.py but in other files or other projects of yours. 1266 02:01:40,666 --> 02:01:44,458 And absolutely, you can create your own library of classes 1267 02:01:44,458 --> 02:01:48,000 by putting the student class in your own module or package, 1268 02:01:48,000 --> 02:01:50,583 per our discussion in the past about libraries more generally. 1269 02:01:50,583 --> 02:01:52,250 And absolutely, you can do that. 1270 02:01:52,250 --> 02:01:54,833 And later today, what we see is we've actually 1271 02:01:54,833 --> 02:01:58,166 been using classes-- you and I-- before, in third party libraries. 1272 02:01:58,166 --> 02:02:00,416 So you, too, can absolutely do the same. 1273 02:02:00,416 --> 02:02:03,666 How about one more question on classes? 1274 02:02:03,666 --> 02:02:06,541 AUDIENCE: Can you have optional variables in classes? 1275 02:02:06,541 --> 02:02:09,666 And two, can you have your own error names, like-- 1276 02:02:09,666 --> 02:02:13,250 let's be egotistical and say I want to raise Eric error? 1277 02:02:13,250 --> 02:02:16,166 DAVID J. MALAN: Short answer, yes. 1278 02:02:16,166 --> 02:02:18,750 These init functions are just like Python functions more 1279 02:02:18,750 --> 02:02:21,000 generally, even though they're special in that they're 1280 02:02:21,000 --> 02:02:23,666 going to get called automatically by Python for you. 1281 02:02:23,666 --> 02:02:25,375 But if you wanted to make house optional, 1282 02:02:25,375 --> 02:02:26,750 you could do something like this. 1283 02:02:26,750 --> 02:02:31,791 You could give it a default value in the init function's signature 1284 02:02:31,791 --> 02:02:34,250 so to speak-- in that first line of code on line two. 1285 02:02:34,250 --> 02:02:36,875 And that would allow me to not have to pass in house. 1286 02:02:36,875 --> 02:02:39,750 In this case, I'm going to continue to always pass in name and house, 1287 02:02:39,750 --> 02:02:41,500 but you could make things optional. 1288 02:02:41,500 --> 02:02:45,458 And yes, to your second question, if you wanted to have your own error message, 1289 02:02:45,458 --> 02:02:50,791 like an Eric error, you could actually create your own Eric error exception. 1290 02:02:50,791 --> 02:02:53,000 And we'll see, in a little bit, that there's actually 1291 02:02:53,000 --> 02:02:58,041 a whole suite of exceptions that exist, and you, too, can invent those as well. 1292 02:02:58,041 --> 02:03:01,375 Let me propose, though, that we now introduce 1293 02:03:01,375 --> 02:03:06,250 one other aspect of this whereby we try printing out what a student looks like. 1294 02:03:06,250 --> 02:03:08,916 At the moment, if I scroll back down to my main function, 1295 02:03:08,916 --> 02:03:11,791 I'm still printing the student's name and house very manually. 1296 02:03:11,791 --> 02:03:14,833 I'm going inside of the object, doing student.name, 1297 02:03:14,833 --> 02:03:17,708 and I'm going inside of the object again and getting student.house, 1298 02:03:17,708 --> 02:03:20,750 just to see where the student is from. 1299 02:03:20,750 --> 02:03:23,666 But wouldn't it be nice if I could just print the student, 1300 02:03:23,666 --> 02:03:25,583 like I've been printing for weeks-- 1301 02:03:25,583 --> 02:03:29,458 any, int, or float, or str, or any other data type? 1302 02:03:29,458 --> 02:03:32,500 Well, let's see what happens if I just try printing the student, 1303 02:03:32,500 --> 02:03:36,166 instead of manually going inside and trying to create that sentence myself. 1304 02:03:36,166 --> 02:03:39,250 Well, in my terminal window-- let me go ahead and run Python of student.py 1305 02:03:39,250 --> 02:03:40,166 again. 1306 02:03:40,166 --> 02:03:41,250 Let me type in Harry. 1307 02:03:41,250 --> 02:03:42,541 Let me type in Gryffindor. 1308 02:03:42,541 --> 02:03:50,333 And voila, Harry-- whoa, OK, main student object at 0x102733e80. 1309 02:03:50,333 --> 02:03:51,916 Well, what is going on? 1310 02:03:51,916 --> 02:03:53,708 Well, if you were to run the same code, you 1311 02:03:53,708 --> 02:03:56,000 might actually see something different on your computer 1312 02:03:56,000 --> 02:03:57,208 in terms of that number. 1313 02:03:57,208 --> 02:04:01,958 But what you're really seeing is the underlying representation, as a string, 1314 02:04:01,958 --> 02:04:03,625 of this specific object. 1315 02:04:03,625 --> 02:04:06,875 In particular, you're seeing where in the computer's memory it is. 1316 02:04:06,875 --> 02:04:12,750 This number, 30x102733e80, refers to, essentially, a specific location 1317 02:04:12,750 --> 02:04:14,541 in the computer's memory or RAM. 1318 02:04:14,541 --> 02:04:19,166 That's not really that interesting for me or you or, generally speaking, 1319 02:04:19,166 --> 02:04:22,250 programmers, but it's just the default way of describing, 1320 02:04:22,250 --> 02:04:25,791 via print, what this thing is. 1321 02:04:25,791 --> 02:04:28,250 But I can override this as well. 1322 02:04:28,250 --> 02:04:31,791 It turns out that there are other special methods in Python 1323 02:04:31,791 --> 02:04:32,958 when it comes to classes-- 1324 02:04:32,958 --> 02:04:36,375 not just underscore underscore, init, underscore underscore, 1325 02:04:36,375 --> 02:04:40,541 but, continuing in that same pattern, underscore underscore, str, 1326 02:04:40,541 --> 02:04:42,041 underscore underscore. 1327 02:04:42,041 --> 02:04:47,000 So this, too, is a special method that, if you define it inside of your class, 1328 02:04:47,000 --> 02:04:50,541 Python will just automatically call this function 1329 02:04:50,541 --> 02:04:56,541 for you any time some other function wants to see your object as a string. 1330 02:04:56,541 --> 02:04:59,750 Print wants to see your object as a string. 1331 02:04:59,750 --> 02:05:02,958 But by default, if you don't have this method defined in your class, 1332 02:05:02,958 --> 02:05:06,416 it's going to print out that very ugly esoteric incarnation thereof, 1333 02:05:06,416 --> 02:05:10,416 where it says main__.Student object at 0x, dot, dot, dot. 1334 02:05:10,416 --> 02:05:13,250 Well, how can I then define my own str function? 1335 02:05:13,250 --> 02:05:17,041 Well, here, back in VS Code, let me propose that I go in 1336 02:05:17,041 --> 02:05:23,416 and define not just __ init, but let me define a second function in this class 1337 02:05:23,416 --> 02:05:25,041 here, as follows-- 1338 02:05:25,041 --> 02:05:28,416 def __ str __. 1339 02:05:28,416 --> 02:05:29,333 There are two. 1340 02:05:29,333 --> 02:05:32,541 Even though the font in VS Code is putting the two underscore so close, 1341 02:05:32,541 --> 02:05:34,375 it just looks like a longer underscore. 1342 02:05:34,375 --> 02:05:37,583 There are indeed two there, on the left and the right just like for init. 1343 02:05:37,583 --> 02:05:42,791 This one only takes one argument that, by convention, is always called self 1344 02:05:42,791 --> 02:05:44,500 so that you have access to it. 1345 02:05:44,500 --> 02:05:47,708 And then, indented below that after a colon, 1346 02:05:47,708 --> 02:05:51,125 I'm going to go ahead and create a format string and return it. 1347 02:05:51,125 --> 02:05:53,500 So let me go ahead and return-- 1348 02:05:53,500 --> 02:05:56,375 how about something generic first like "a student." 1349 02:05:56,375 --> 02:05:58,916 So I'm not going to bother even trying to figure out 1350 02:05:58,916 --> 02:06:00,625 what this student's name or house is. 1351 02:06:00,625 --> 02:06:02,875 I'm just going to always return "a student." 1352 02:06:02,875 --> 02:06:08,666 Let me go back now to my earlier code, which has print (student) on line 16. 1353 02:06:08,666 --> 02:06:13,041 Let me clear my terminal window and rerun Python of student.py, Enter. 1354 02:06:13,041 --> 02:06:14,958 Type in Harry, type in Gryffindor. 1355 02:06:14,958 --> 02:06:17,333 Last time, I saw that very cryptic output. 1356 02:06:17,333 --> 02:06:20,958 This time, I see, more generically, "a student." 1357 02:06:20,958 --> 02:06:23,375 More readable but not very enlightening. 1358 02:06:23,375 --> 02:06:24,541 Which student is this? 1359 02:06:24,541 --> 02:06:31,791 Well, notice that the double underscore str method takes in this self argument 1360 02:06:31,791 --> 02:06:36,708 by default. It's just the way the Python authors designed this method. 1361 02:06:36,708 --> 02:06:41,833 It will always be passed a reference to the current student object. 1362 02:06:41,833 --> 02:06:43,000 What do I mean by that? 1363 02:06:43,000 --> 02:06:45,916 When this line of code on line 6 is called, 1364 02:06:45,916 --> 02:06:48,666 print, because it's hoping it's going to get a string, 1365 02:06:48,666 --> 02:06:52,750 is going to trigger the underscore underscore, str, underscore 1366 02:06:52,750 --> 02:06:54,458 underscore method to be called. 1367 02:06:54,458 --> 02:06:58,416 And Python, for you, automatically is going to pass into that method 1368 02:06:58,416 --> 02:07:01,208 a reference to the object that's trying to be 1369 02:07:01,208 --> 02:07:05,291 printed so that you, the programmer, can do something like this. 1370 02:07:05,291 --> 02:07:08,000 Here's an f string with double quotes as usual. 1371 02:07:08,000 --> 02:07:10,458 I'm going to use some curly braces and say print out 1372 02:07:10,458 --> 02:07:14,250 self.name from self.house. 1373 02:07:14,250 --> 02:07:17,458 So there's nothing new in what I've just done. 1374 02:07:17,458 --> 02:07:19,958 It's just an f string-- an f on the beginning, 1375 02:07:19,958 --> 02:07:22,708 two double quotes, a couple of pairs of curly braces. 1376 02:07:22,708 --> 02:07:28,750 But because, automatically, this str method gets passed self, so to speak, 1377 02:07:28,750 --> 02:07:32,333 a reference to the current object, I can go inside of that object 1378 02:07:32,333 --> 02:07:33,083 and grab the name. 1379 02:07:33,083 --> 02:07:36,000 I can go inside that object again and grab the house. 1380 02:07:36,000 --> 02:07:38,791 So now, when I go back to my terminal window-- 1381 02:07:38,791 --> 02:07:41,041 previously it just printed out a student. 1382 02:07:41,041 --> 02:07:44,416 But now, if I run Python of student.py, Enter-- 1383 02:07:44,416 --> 02:07:48,083 type in Harry, type in Gryffindor, and one more time hit Enter, 1384 02:07:48,083 --> 02:07:50,750 Harry is again from Gryffindor. 1385 02:07:50,750 --> 02:07:52,500 But if I run this yet again-- 1386 02:07:52,500 --> 02:07:57,125 let's, for instance, do Draco is from Slytherin, Enter. 1387 02:07:57,125 --> 02:07:58,625 Draco's from Slytherin. 1388 02:07:58,625 --> 02:08:04,291 Now it's customized to the specific object that we're trying to print. 1389 02:08:04,291 --> 02:08:08,250 Questions on this function here-- 1390 02:08:08,250 --> 02:08:11,666 this Dunder str method. 1391 02:08:11,666 --> 02:08:14,541 AUDIENCE: Is there anything else that the underscore underscore, 1392 02:08:14,541 --> 02:08:16,666 str method can do? 1393 02:08:16,666 --> 02:08:19,416 The other question is, what's the difference between str and repr. 1394 02:08:19,416 --> 02:08:20,750 DAVID J. MALAN: A good question. 1395 02:08:20,750 --> 02:08:24,458 So there are many other methods that come with Python classes 1396 02:08:24,458 --> 02:08:26,041 that start with underscore underscore. 1397 02:08:26,041 --> 02:08:27,958 We're just scratching the surface, and we'll pretty much 1398 02:08:27,958 --> 02:08:29,083 focus primarily on these. 1399 02:08:29,083 --> 02:08:30,833 But yes, there are many others, and we'll 1400 02:08:30,833 --> 02:08:33,666 see at least one other in just a little bit. 1401 02:08:33,666 --> 02:08:37,375 Among the others is one called repr, which is 1402 02:08:37,375 --> 02:08:39,583 a representation of the Python object. 1403 02:08:39,583 --> 02:08:43,000 Generally speaking, the underscore underscore, repr, 1404 02:08:43,000 --> 02:08:45,791 underscore underscore method is meant for developers' eyes. 1405 02:08:45,791 --> 02:08:49,166 It typically has more information than "Harry from Gryffindor." 1406 02:08:49,166 --> 02:08:53,083 It would also say what type of object it is, like a student, capital 1407 02:08:53,083 --> 02:08:56,583 S, whereas underscore underscore, str, underscore underscore is generally 1408 02:08:56,583 --> 02:08:58,125 meant for users-- 1409 02:08:58,125 --> 02:09:01,500 the users of the program, and it's meant to be even more user-friendly. 1410 02:09:01,500 --> 02:09:04,208 But both of those can be overridden as you see fit. 1411 02:09:04,208 --> 02:09:08,041 Well, let me propose now that we pick up where we've left off on student 1412 02:09:08,041 --> 02:09:11,958 and just add even more functionality, but not just these special methods 1413 02:09:11,958 --> 02:09:15,166 like double underscore init and double underscore str. 1414 02:09:15,166 --> 02:09:18,750 Let's create our own methods because therein lies the real power 1415 02:09:18,750 --> 02:09:21,875 and flexibility of classes if you and I as the programmers 1416 02:09:21,875 --> 02:09:25,291 can invent new functionality that's specific to students. 1417 02:09:25,291 --> 02:09:29,125 For instance, students at Hogwarts, over the time in school, 1418 02:09:29,125 --> 02:09:31,500 learn how to cast a certain type of spell. 1419 02:09:31,500 --> 02:09:34,083 So when they say, Expecto Patronum, something 1420 02:09:34,083 --> 02:09:36,083 comes out of their wand that typically resembles 1421 02:09:36,083 --> 02:09:37,458 an animal or something like that. 1422 02:09:37,458 --> 02:09:40,000 It's a special spell that they have to practice and practice. 1423 02:09:40,000 --> 02:09:44,041 So let's see if we can't store, not just the student's name and their house, 1424 02:09:44,041 --> 02:09:47,041 but also their "patronus," what, actually, they 1425 02:09:47,041 --> 02:09:48,625 conjure when using this spell. 1426 02:09:48,625 --> 02:09:50,750 Well, let me go ahead and clear my terminal window. 1427 02:09:50,750 --> 02:09:56,583 And in the top of my code here, in the init method of Student, let me go ahead 1428 02:09:56,583 --> 02:10:00,875 and start expecting a third argument, in addition to self, which automatically 1429 02:10:00,875 --> 02:10:02,833 gets passed in, called patronus. 1430 02:10:02,833 --> 02:10:06,125 And I'm not going to worry, for now, on validating 1431 02:10:06,125 --> 02:10:10,041 the patronus from an official list of valid patronuses, or patroni. 1432 02:10:10,041 --> 02:10:13,041 I'm instead going to go ahead and just blindly assign 1433 02:10:13,041 --> 02:10:16,458 it to self.patronus = patronus, and we're 1434 02:10:16,458 --> 02:10:19,083 going to let the user type whatever they want for now. 1435 02:10:19,083 --> 02:10:21,333 But I could certainly add more error checking 1436 02:10:21,333 --> 02:10:25,583 if I wanted to limit the patronus to a specific list of them here. 1437 02:10:25,583 --> 02:10:29,000 Let me go ahead now and prompt the user for this patronus, 1438 02:10:29,000 --> 02:10:33,666 as by-- in my get_student function-- defining a variable 1439 02:10:33,666 --> 02:10:36,375 called patronus or anything else, prompting the user 1440 02:10:36,375 --> 02:10:38,333 for input for their patronus. 1441 02:10:38,333 --> 02:10:42,708 And now I'm going to go ahead and pass in that third variable here. 1442 02:10:42,708 --> 02:10:46,041 So again, similar in spirit to just adding more and more attributes 1443 02:10:46,041 --> 02:10:47,875 to the class, I'm going to pass in all three 1444 02:10:47,875 --> 02:10:49,791 of these values instead of just two. 1445 02:10:49,791 --> 02:10:52,333 I'm not going to do anything interesting with that value yet. 1446 02:10:52,333 --> 02:10:55,583 But just to make sure I haven't made things worse by breaking my code, 1447 02:10:55,583 --> 02:10:57,875 let me run Python of student.py. 1448 02:10:57,875 --> 02:10:59,083 I'll type in Harry. 1449 02:10:59,083 --> 02:11:00,416 I'll type in Gryffindor. 1450 02:11:00,416 --> 02:11:02,500 And it turns out his patronus was a stag, . 1451 02:11:02,500 --> 02:11:06,125 And hit Enter I haven't seen what his patronus is in my output 1452 02:11:06,125 --> 02:11:08,708 because I didn't change my str method yet. 1453 02:11:08,708 --> 02:11:10,625 But at least I don't have any syntax errors. 1454 02:11:10,625 --> 02:11:12,958 So at least I've not made anything worse. 1455 02:11:12,958 --> 02:11:16,416 But suppose, now, I want to have functionality, not just 1456 02:11:16,416 --> 02:11:19,708 for initializing a student and printing out a student. 1457 02:11:19,708 --> 02:11:23,666 If my class is really meant to be a student, what I can do 1458 02:11:23,666 --> 02:11:27,416 is not just remember information about data about students. 1459 02:11:27,416 --> 02:11:31,291 What's powerful about classes, unlike dictionaries alone, 1460 02:11:31,291 --> 02:11:36,708 is that classes can have, not just variables or instance variables-- 1461 02:11:36,708 --> 02:11:38,625 those attributes we keep creating. 1462 02:11:38,625 --> 02:11:42,458 They can also have functions built in, a.k.a. 1463 02:11:42,458 --> 02:11:43,083 methods. 1464 02:11:43,083 --> 02:11:45,958 When a function is inside of a class, it's called a "method," 1465 02:11:45,958 --> 02:11:47,666 but it's still just a function. 1466 02:11:47,666 --> 02:11:51,000 At this point, we've seen two functions already-- two methods-- 1467 02:11:51,000 --> 02:11:54,958 called a double underscore init and double underscore str, 1468 02:11:54,958 --> 02:11:57,958 but those are special methods in that they just work. 1469 02:11:57,958 --> 02:12:01,416 If you define them, Python calls them automatically for you. 1470 02:12:01,416 --> 02:12:04,958 But what if you wanted to create more functionality for a student 1471 02:12:04,958 --> 02:12:09,000 so that your class really represents this real world, or maybe "fantasy" 1472 02:12:09,000 --> 02:12:13,375 world notion of a student, where students not only have names and houses 1473 02:12:13,375 --> 02:12:16,375 and patronuses; they also have functionality. 1474 02:12:16,375 --> 02:12:22,541 They have actions they can perform like casting a charm, a spell, magically. 1475 02:12:22,541 --> 02:12:26,333 Could we implement, therefore, a function called charm, that 1476 02:12:26,333 --> 02:12:30,291 actually uses their magical knowledge? 1477 02:12:30,291 --> 02:12:33,791 Well, let's go ahead and define our very own function as follows. 1478 02:12:33,791 --> 02:12:37,583 Let me clear my terminal window, scroll back up to my student class. 1479 02:12:37,583 --> 02:12:40,916 And instead of creating yet another function that's special, 1480 02:12:40,916 --> 02:12:45,416 with double underscores, I'm going to invent my own function, or method, 1481 02:12:45,416 --> 02:12:46,916 inside of this class. 1482 02:12:46,916 --> 02:12:51,500 I want to give Harry and Hermione and all of the other students 1483 02:12:51,500 --> 02:12:55,000 the ability to cast charms, so I'm going to define a function that I 1484 02:12:55,000 --> 02:12:57,291 can completely, on my own, call charm. 1485 02:12:57,291 --> 02:12:59,125 I could call this function anything I want. 1486 02:12:59,125 --> 02:13:01,708 But because it's a method inside of a class, 1487 02:13:01,708 --> 02:13:06,083 the convention is that it's always going to take at least one argument, called 1488 02:13:06,083 --> 02:13:11,000 self, by convention so that you have access to the current object, 1489 02:13:11,000 --> 02:13:13,083 even if you don't plan to use it, per se. 1490 02:13:13,083 --> 02:13:16,708 All right, let me go ahead and propose that we implement charm in such a way 1491 02:13:16,708 --> 02:13:21,750 that the method returns an emoji that's appropriate for each student's 1492 02:13:21,750 --> 02:13:23,125 patronus. 1493 02:13:23,125 --> 02:13:25,750 How to implement this-- well, inside of the charm method, 1494 02:13:25,750 --> 02:13:29,041 let's go ahead and match on self.patronus, 1495 02:13:29,041 --> 02:13:31,416 which is the instance variable containing a string that 1496 02:13:31,416 --> 02:13:33,458 represents each student's patronus. 1497 02:13:33,458 --> 02:13:38,208 And in the case that it matches a stag, for instance, for Harry, let's go ahead 1498 02:13:38,208 --> 02:13:40,333 and return maybe the closest emoji-- 1499 02:13:40,333 --> 02:13:41,750 this horse here. 1500 02:13:41,750 --> 02:13:44,625 How about in the case of an otter? 1501 02:13:44,625 --> 02:13:49,708 Well, in that case, let's go ahead and return, oh, maybe the closest match 1502 02:13:49,708 --> 02:13:52,583 to the otter, which might be this emoji here. 1503 02:13:52,583 --> 02:13:57,000 And let's see, in the case of a-- for Ron, rather than Hermione-- 1504 02:13:57,000 --> 02:14:02,791 a Jack Russell terrier, let's go ahead and return-- 1505 02:14:02,791 --> 02:14:04,625 don't have as many options here. 1506 02:14:04,625 --> 02:14:08,750 Why don't we go ahead and return the cutest available dog in that case. 1507 02:14:08,750 --> 02:14:14,250 And in the case of no patronus recognized, 1508 02:14:14,250 --> 02:14:16,375 as might cover someone like Draco, let's go ahead 1509 02:14:16,375 --> 02:14:19,750 and use a default case using the underscore as in the past, 1510 02:14:19,750 --> 02:14:22,791 and let's go ahead and return for this-- oh, what should happen 1511 02:14:22,791 --> 02:14:24,250 if someone doesn't have a patronus? 1512 02:14:24,250 --> 02:14:26,541 Why don't we just see a magical wand that 1513 02:14:26,541 --> 02:14:28,958 seems to fizzle out, as in this case? 1514 02:14:28,958 --> 02:14:31,333 All right, well, now, rather than just print the student, 1515 02:14:31,333 --> 02:14:33,375 let's go about printing their actual patronus. 1516 02:14:33,375 --> 02:14:36,000 So I'm going to go down to my main function here. 1517 02:14:36,000 --> 02:14:39,250 I'm going to still get a student, using the get_student function. 1518 02:14:39,250 --> 02:14:44,208 But rather than print student, let's go ahead and declare "Expecto Patronum!" 1519 02:14:44,208 --> 02:14:46,791 printing out just that as pure text. 1520 02:14:46,791 --> 02:14:50,750 And now let's go ahead and print out, not the student but, rather, 1521 02:14:50,750 --> 02:14:54,416 the return value of their own charm method. 1522 02:14:54,416 --> 02:14:58,583 So let me go back down to my terminal window and run Python of student.py 1523 02:14:58,583 --> 02:14:59,708 and Enter. 1524 02:14:59,708 --> 02:15:01,250 Name-- let's start with Harry. 1525 02:15:01,250 --> 02:15:03,500 He lives in Gryffindor. 1526 02:15:03,500 --> 02:15:05,083 Patronus is a stag. 1527 02:15:05,083 --> 02:15:06,291 And let's see-- 1528 02:15:06,291 --> 02:15:08,125 Expecto Patronum! 1529 02:15:08,125 --> 02:15:10,708 And of course, we'd see the stag emoji. 1530 02:15:10,708 --> 02:15:13,333 What about someone like Draco, who, at least in the books, 1531 02:15:13,333 --> 02:15:15,333 doesn't have a known patronus? 1532 02:15:15,333 --> 02:15:17,416 Well, let's go ahead and clear my terminal window, 1533 02:15:17,416 --> 02:15:22,625 rerun Python of student.py, and this time, let's type in Draco for name, 1534 02:15:22,625 --> 02:15:25,750 Slytherin for house, and Patronus is unknown. 1535 02:15:25,750 --> 02:15:27,583 So I'm just going to go ahead and hit Enter. 1536 02:15:27,583 --> 02:15:29,666 And now, Expecto Patronum! 1537 02:15:29,666 --> 02:15:32,916 And it just of sizzles instead. 1538 02:15:32,916 --> 02:15:38,208 Well, let me propose, now, that we remove this patronus code just 1539 02:15:38,208 --> 02:15:41,833 to simplify our world and focus on some of the other core 1540 02:15:41,833 --> 02:15:43,208 capabilities of classes. 1541 02:15:43,208 --> 02:15:44,958 So at the risk of disappointing, I'm going 1542 02:15:44,958 --> 02:15:47,916 to get rid of all of these beautiful emoji and charms, 1543 02:15:47,916 --> 02:15:51,875 and I'm going to go ahead and stop asking the user now for their patronus. 1544 02:15:51,875 --> 02:15:54,708 And I'm going to stop passing it into init here. 1545 02:15:54,708 --> 02:15:57,541 And I'm going to stop doing this here. 1546 02:15:57,541 --> 02:16:03,333 And I'm going to instead just go ahead and restore our use of print student 1547 02:16:03,333 --> 02:16:06,541 here, and I'm going to go ahead and get rid of patronus down here. 1548 02:16:06,541 --> 02:16:10,541 So just essentially undo all of the fun charms we just created. 1549 02:16:10,541 --> 02:16:16,291 So we're now back at the point in the story where we have a student class, 1550 02:16:16,291 --> 02:16:18,041 with only two methods-- 1551 02:16:18,041 --> 02:16:19,416 init and str. 1552 02:16:19,416 --> 02:16:21,375 The first of those takes, of course, self 1553 02:16:21,375 --> 02:16:25,125 as the first argument as it always will, plus two more now-- name and house, 1554 02:16:25,125 --> 02:16:26,208 no more patronus. 1555 02:16:26,208 --> 02:16:28,041 We're validating name up here. 1556 02:16:28,041 --> 02:16:29,833 We're validating house down here. 1557 02:16:29,833 --> 02:16:32,375 And then we're assigning name and house, respectively, 1558 02:16:32,375 --> 02:16:35,625 to two instance variables called name and house also. 1559 02:16:35,625 --> 02:16:38,875 But we use self to get access to the current object, 1560 02:16:38,875 --> 02:16:40,750 to store those values therein. 1561 02:16:40,750 --> 02:16:44,583 We then still have our str method here, which takes one argument-- 1562 02:16:44,583 --> 02:16:46,208 by default, self, and that's it. 1563 02:16:46,208 --> 02:16:49,750 And that function is going to be called automatically any time 1564 02:16:49,750 --> 02:16:52,625 you want to convert a student object to a string, 1565 02:16:52,625 --> 02:16:55,125 just like print might want to do here. 1566 02:16:55,125 --> 02:16:57,791 So let me go ahead and just make sure I haven't broken anything. 1567 02:16:57,791 --> 02:16:59,750 Let me run Python of student.py. 1568 02:16:59,750 --> 02:17:00,916 I'll type in Harry. 1569 02:17:00,916 --> 02:17:03,041 I'll type in Gryffindor, Enter. 1570 02:17:03,041 --> 02:17:04,416 OK, we're back in business. 1571 02:17:04,416 --> 02:17:08,166 Gone are the charms and patronus, but at least I'm back to a situation 1572 02:17:08,166 --> 02:17:10,250 where I have names and houses. 1573 02:17:10,250 --> 02:17:14,416 But it turns out, at the moment, our use of classes 1574 02:17:14,416 --> 02:17:20,083 is not very robust, even though we have this mechanism, very cleverly, 1575 02:17:20,083 --> 02:17:23,041 if I may, in our init method of making sure 1576 02:17:23,041 --> 02:17:27,125 that we're validating name and house, making sure that name is not blank, 1577 02:17:27,125 --> 02:17:32,625 and making sure that house is a valid house among those four Hogwarts houses. 1578 02:17:32,625 --> 02:17:35,208 It turns out that classes will still let me 1579 02:17:35,208 --> 02:17:38,541 get at those attributes, those so-called instance variables, 1580 02:17:38,541 --> 02:17:40,750 using dot notation anyway. 1581 02:17:40,750 --> 02:17:44,500 Let me scroll down then and try to do this a little adversarially. 1582 02:17:44,500 --> 02:17:48,500 Suppose that online 16 I go ahead and call get_student, 1583 02:17:48,500 --> 02:17:52,541 which exists as before, and then I store the return value in a student 1584 02:17:52,541 --> 02:17:54,791 variable-- again, on line 16. 1585 02:17:54,791 --> 02:18:00,291 That will ensure that get_student gets called, which calls input and input. 1586 02:18:00,291 --> 02:18:02,916 And then it calls the student constructor, 1587 02:18:02,916 --> 02:18:05,541 which invokes, automatically, this init method. 1588 02:18:05,541 --> 02:18:08,458 So by way of how we've laid out my code, we're 1589 02:18:08,458 --> 02:18:11,750 going to ensure that name is not blank and house is definitely 1590 02:18:11,750 --> 02:18:12,916 one of those four values. 1591 02:18:12,916 --> 02:18:16,541 My error correction-- or error checking is in place. 1592 02:18:16,541 --> 02:18:20,708 But if I'm a little adversarial, I can still circumvent it. 1593 02:18:20,708 --> 02:18:25,541 Suppose that-- fine, going to require me to type in Harry and Gryffindor? 1594 02:18:25,541 --> 02:18:28,083 I'm going to go ahead and type in student.house 1595 02:18:28,083 --> 02:18:31,541 equals, quote, unquote, "Number Four, Privet Drive," 1596 02:18:31,541 --> 02:18:33,333 and you're not going to be able to stop me. 1597 02:18:33,333 --> 02:18:34,083 Why? 1598 02:18:34,083 --> 02:18:36,708 Well, it turns out, with classes and objects 1599 02:18:36,708 --> 02:18:40,375 thereof, you and I can still access those instance variables 1600 02:18:40,375 --> 02:18:42,083 using this familiar dot notation. 1601 02:18:42,083 --> 02:18:45,125 That's how we began the story of classes-- just setting these attributes 1602 02:18:45,125 --> 02:18:45,875 ourselves. 1603 02:18:45,875 --> 02:18:48,541 But you can also read these attributes themselves 1604 02:18:48,541 --> 02:18:50,750 and change them later if you want. 1605 02:18:50,750 --> 02:18:55,833 And this will effectively circumvent the if condition and the other if condition 1606 02:18:55,833 --> 02:19:00,000 in our init method because that is only called when you first 1607 02:19:00,000 --> 02:19:02,125 create the student object. 1608 02:19:02,125 --> 02:19:04,541 There's nothing stopping me, at the moment, 1609 02:19:04,541 --> 02:19:08,291 from just changing the house or the name after. 1610 02:19:08,291 --> 02:19:12,541 So if I now clear my terminal window and run Python of student.py, 1611 02:19:12,541 --> 02:19:16,208 I'll still type in Harry and Gryffindor to meet my requirements 1612 02:19:16,208 --> 02:19:17,916 that the house be one of those four. 1613 02:19:17,916 --> 02:19:21,375 But when it's printed, notice, I've still overridden it. 1614 02:19:21,375 --> 02:19:26,083 So it seems that, while classes do allow us a little more control over the data 1615 02:19:26,083 --> 02:19:30,250 we're storing, it doesn't necessarily prevent the user-- 1616 02:19:30,250 --> 02:19:33,625 or rather the programmer-- be it myself or maybe a colleague, 1617 02:19:33,625 --> 02:19:35,791 from still messing things up. 1618 02:19:35,791 --> 02:19:39,083 So here, too, in the spirit of programming a little more 1619 02:19:39,083 --> 02:19:43,916 defensively, allow me to introduce another feature of Python as well-- 1620 02:19:43,916 --> 02:19:46,208 namely properties. 1621 02:19:46,208 --> 02:19:49,375 So a property is really just an attribute 1622 02:19:49,375 --> 02:19:52,750 that has even more defense mechanisms put into place, 1623 02:19:52,750 --> 02:19:58,208 a little more functionality implemented by you to prevent programmers, like me 1624 02:19:58,208 --> 02:20:01,083 and you, from messing things up like these attributes. 1625 02:20:01,083 --> 02:20:04,375 So again, a property is going to be an attribute that you and I just 1626 02:20:04,375 --> 02:20:06,083 have more control over. 1627 02:20:06,083 --> 02:20:06,750 How? 1628 02:20:06,750 --> 02:20:09,791 We just write a little more code, using some Python conventions. 1629 02:20:09,791 --> 02:20:14,333 And how we're going to do that is going to use, in just a moment, a feature-- 1630 02:20:14,333 --> 02:20:18,083 a keyword known as @property, which is technically a function. 1631 02:20:18,083 --> 02:20:19,916 Property is a function in Python. 1632 02:20:19,916 --> 02:20:24,791 But we're about to see some new @ syntax that allows you to decorate functions. 1633 02:20:24,791 --> 02:20:26,125 And this, too, is a term of art. 1634 02:20:26,125 --> 02:20:28,916 In the world of Python, you can have decorators, 1635 02:20:28,916 --> 02:20:33,166 which are functions that modify the behavior of other functions, 1636 02:20:33,166 --> 02:20:36,708 if you will, and we'll leave it at that without going too much into the weeds. 1637 02:20:36,708 --> 02:20:39,291 And we'll see, by, example how you can use these decorators, 1638 02:20:39,291 --> 02:20:41,541 specifically to define properties. 1639 02:20:41,541 --> 02:20:43,708 So let me go back to VS Code here. 1640 02:20:43,708 --> 02:20:46,791 And let me propose that I do this. 1641 02:20:46,791 --> 02:20:49,375 I'm going to go ahead and create-- 1642 02:20:49,375 --> 02:20:54,291 how about a property called house as follows. 1643 02:20:54,291 --> 02:21:00,833 Inside of my student class, I'm going to go ahead-- and below my init method 1644 02:21:00,833 --> 02:21:03,958 and below my str method, I'm going to go ahead and define 1645 02:21:03,958 --> 02:21:09,000 a function called house that takes, as it always must, one argument at least, 1646 02:21:09,000 --> 02:21:10,041 called self. 1647 02:21:10,041 --> 02:21:15,458 And what I'm going to do now is return self.house. 1648 02:21:15,458 --> 02:21:17,625 So I'm just going to define a method called 1649 02:21:17,625 --> 02:21:23,375 house, whose sole purpose in life is to return the value of house. 1650 02:21:23,375 --> 02:21:27,375 But I'm going to define one other method, curiously also called house, 1651 02:21:27,375 --> 02:21:31,375 but that's going to take into, as arguments, two values-- 1652 02:21:31,375 --> 02:21:35,625 self as always and also a value called house. 1653 02:21:35,625 --> 02:21:37,291 And I'm going to now do this. 1654 02:21:37,291 --> 02:21:40,375 1655 02:21:40,375 --> 02:21:43,958 I'm going to do self.house = house. 1656 02:21:43,958 --> 02:21:45,250 Now, what have I done? 1657 02:21:45,250 --> 02:21:47,625 Well, let me just temporarily add some comments here. 1658 02:21:47,625 --> 02:21:50,166 In a moment, we're going to start referring to this generally 1659 02:21:50,166 --> 02:21:51,083 as a getter. 1660 02:21:51,083 --> 02:21:54,083 And down here, I'm going to refer to this as a setter. 1661 02:21:54,083 --> 02:21:56,875 And this is terminology frequently see in the world of Java. 1662 02:21:56,875 --> 02:21:58,791 Some of you have programmed in Java before. 1663 02:21:58,791 --> 02:22:01,875 But as the names imply, a getter is a function 1664 02:22:01,875 --> 02:22:05,333 for a class that gets some attributes. 1665 02:22:05,333 --> 02:22:10,291 A setter is a function in some class that sets some value. 1666 02:22:10,291 --> 02:22:14,125 And now, even though we're not done, and there's a bit of a mistake in the code 1667 02:22:14,125 --> 02:22:17,916 I've already written, intuitively, what we're going to do is this. 1668 02:22:17,916 --> 02:22:21,000 We're trying to prevent programmers, myself included, 1669 02:22:21,000 --> 02:22:23,541 from circumventing my error checking that I 1670 02:22:23,541 --> 02:22:25,666 put into place for name and house. 1671 02:22:25,666 --> 02:22:26,958 How can I do that? 1672 02:22:26,958 --> 02:22:29,500 Well, we don't have that many building blocks in programming. 1673 02:22:29,500 --> 02:22:33,541 We have things like variables for data, and we have functions for actions. 1674 02:22:33,541 --> 02:22:35,000 Well, why don't we do this? 1675 02:22:35,000 --> 02:22:39,958 Why don't we somehow require that, in order to access an attribute, 1676 02:22:39,958 --> 02:22:41,375 you go through some function. 1677 02:22:41,375 --> 02:22:44,583 And let's require that, in order to set some attribute, 1678 02:22:44,583 --> 02:22:46,291 you go through some function. 1679 02:22:46,291 --> 02:22:51,958 And conventionally, those functions are called a getter function and a setter 1680 02:22:51,958 --> 02:22:52,708 function. 1681 02:22:52,708 --> 02:22:56,541 And why are we using functions or, in this case, methods inside of a class? 1682 02:22:56,541 --> 02:23:00,000 Well, once you have functions, those are just actions or verbs 1683 02:23:00,000 --> 02:23:01,583 that you and I can create ourselves. 1684 02:23:01,583 --> 02:23:05,208 We can put any error correction I want in these functions 1685 02:23:05,208 --> 02:23:08,416 because it's code that's going to get executed top to bottom. 1686 02:23:08,416 --> 02:23:15,541 So how can I now prevent the user from setting the house to an invalid value? 1687 02:23:15,541 --> 02:23:20,500 Let me borrow some logic from before rather than blindly do this-- just set 1688 02:23:20,500 --> 02:23:25,291 self.house equal to the house value that's passed in-- let's add our error 1689 02:23:25,291 --> 02:23:26,166 checking there. 1690 02:23:26,166 --> 02:23:34,791 So if house is not in the following list of Gryffindor or Hufflepuff 1691 02:23:34,791 --> 02:23:40,708 or Ravenclaw or Slytherin, just as before, 1692 02:23:40,708 --> 02:23:44,541 let's go ahead and raise a value error, just to signify that, uh-uh, something 1693 02:23:44,541 --> 02:23:45,291 has gone wrong. 1694 02:23:45,291 --> 02:23:46,333 I'll be more explicit. 1695 02:23:46,333 --> 02:23:50,541 I'll include a message like, "invalid house," quote, unquote. 1696 02:23:50,541 --> 02:23:56,166 Otherwise, I'm going to proceed on, now, line 21 to set self.house to house. 1697 02:23:56,166 --> 02:24:00,416 So I've just copied, if you will, or retyped my error checking inside 1698 02:24:00,416 --> 02:24:02,333 of this so-called setter function. 1699 02:24:02,333 --> 02:24:03,666 Now, why have I done that? 1700 02:24:03,666 --> 02:24:07,458 Well, to be clear, whenever the user or the programmer 1701 02:24:07,458 --> 02:24:12,375 writes code like this, student.house equals, what's 1702 02:24:12,375 --> 02:24:16,166 about to happen magically is Python will not just 1703 02:24:16,166 --> 02:24:19,916 let the programmer access student house directly-- that attribute, 1704 02:24:19,916 --> 02:24:21,750 that instance variable, a.k.a. 1705 02:24:21,750 --> 02:24:22,875 self house. 1706 02:24:22,875 --> 02:24:26,833 It's instead going to magically automatically call 1707 02:24:26,833 --> 02:24:28,791 this setter function for me. 1708 02:24:28,791 --> 02:24:30,708 How does Python know to do that? 1709 02:24:30,708 --> 02:24:35,666 Well, if it's see that, on the left-hand side, there is self.house, 1710 02:24:35,666 --> 02:24:42,125 where house is the name of the getter or setter, and then it sees an equal sign, 1711 02:24:42,125 --> 02:24:45,625 indicating assignment, that's just enough of a visual clue to say, 1712 02:24:45,625 --> 02:24:46,208 wait a minute. 1713 02:24:46,208 --> 02:24:49,250 I'm not going to let you access that attribute directly. 1714 02:24:49,250 --> 02:24:51,791 I'm going to use the setter instead. 1715 02:24:51,791 --> 02:24:52,291 Why? 1716 02:24:52,291 --> 02:24:54,291 Because the equal sign means I'm trying to set. 1717 02:24:54,291 --> 02:24:58,250 I'm trying to assign a value from right to left into that attribute. 1718 02:24:58,250 --> 02:25:00,500 So what Python's is going to do automatically 1719 02:25:00,500 --> 02:25:02,083 is call this function for me. 1720 02:25:02,083 --> 02:25:06,708 And that's amazing because now I can execute code-- an algorithm to check, 1721 02:25:06,708 --> 02:25:08,625 do I want to let the user-- 1722 02:25:08,625 --> 02:25:12,041 the programmer set that attribute to that value? 1723 02:25:12,041 --> 02:25:13,916 If not, I'm going to raise a value error, 1724 02:25:13,916 --> 02:25:15,833 and you're just not going to be able to do it. 1725 02:25:15,833 --> 02:25:17,500 If so fine. 1726 02:25:17,500 --> 02:25:19,416 I'll go ahead and set it for you. 1727 02:25:19,416 --> 02:25:21,958 But in order to do this, we need a little more syntax. 1728 02:25:21,958 --> 02:25:25,375 And I'm going to get rid of my comment, and I'm going to use that decorator. 1729 02:25:25,375 --> 02:25:29,583 I need to tell Python to treat this method as a getter. 1730 02:25:29,583 --> 02:25:32,083 And then the syntax for the setter is a little different. 1731 02:25:32,083 --> 02:25:34,500 You now say house.setter. 1732 02:25:34,500 --> 02:25:37,500 I wish one was getter and the other was setter. 1733 02:25:37,500 --> 02:25:39,458 That's not the way they designed it. 1734 02:25:39,458 --> 02:25:44,291 When you want to define a getter, you just say @property above the function. 1735 02:25:44,291 --> 02:25:48,833 And you name the function exactly like you would like the property 1736 02:25:48,833 --> 02:25:51,000 to be called-- quote, unquote, "house." 1737 02:25:51,000 --> 02:25:54,416 Once you do that, you can now use a new decorator 1738 02:25:54,416 --> 02:25:57,625 that's automatically created for you called @house, 1739 02:25:57,625 --> 02:25:58,750 because I called it house. 1740 02:25:58,750 --> 02:26:01,416 And then you literally say, @house.setter. 1741 02:26:01,416 --> 02:26:05,333 And this whole line, on line 17, is a clue to Python 1742 02:26:05,333 --> 02:26:08,750 that here comes a function, whose name is identical-- 1743 02:26:08,750 --> 02:26:12,541 but notice that it takes two arguments-- both self, so you have access 1744 02:26:12,541 --> 02:26:15,208 to the contents of the object, and house, which is just 1745 02:26:15,208 --> 02:26:20,041 going to be a str that comes from the programmer from the human input return 1746 02:26:20,041 --> 02:26:23,958 value so that you can set that value as well. 1747 02:26:23,958 --> 02:26:27,375 But there's one fix I need to make now, here. 1748 02:26:27,375 --> 02:26:29,625 Everything else, I think, is still good. 1749 02:26:29,625 --> 02:26:31,250 However watch this. 1750 02:26:31,250 --> 02:26:34,500 I no longer need this error check here. 1751 02:26:34,500 --> 02:26:35,333 Why? 1752 02:26:35,333 --> 02:26:39,375 Because, if I scroll back down to my code here, 1753 02:26:39,375 --> 02:26:46,250 I claimed a moment ago that code like this, with student.house equals, 1754 02:26:46,250 --> 02:26:49,666 is going to automatically get Python to call my setter for me. 1755 02:26:49,666 --> 02:26:50,666 Guess what? 1756 02:26:50,666 --> 02:26:55,875 Even up here, in my init method, calling self.house equals 1757 02:26:55,875 --> 02:27:00,208 is also going to call my setter method, which is amazing 1758 02:27:00,208 --> 02:27:06,083 because now I can keep all of my error checking in one place in the setter, 1759 02:27:06,083 --> 02:27:10,833 and it will now get called either when I create the object for the first time, 1760 02:27:10,833 --> 02:27:14,541 because of init, or even if the programmer 1761 02:27:14,541 --> 02:27:19,375 tries to circumvent that init method and change the value of this attribute, 1762 02:27:19,375 --> 02:27:21,041 my setter will also get called. 1763 02:27:21,041 --> 02:27:26,000 My setter will get called any time I access .house. 1764 02:27:26,000 --> 02:27:28,458 But there's one fix I need to make. 1765 02:27:28,458 --> 02:27:32,958 Unfortunately, I have collided names. 1766 02:27:32,958 --> 02:27:38,458 Right now, if we go up here, on line 5, this is an instance variable. 1767 02:27:38,458 --> 02:27:42,333 It's a string inside of my self, inside of the current student object, 1768 02:27:42,333 --> 02:27:43,083 called name. 1769 02:27:43,083 --> 02:27:46,958 And this is another instance variable called house. 1770 02:27:46,958 --> 02:27:51,291 Unfortunately, if I have an instance variable called name and house, 1771 02:27:51,291 --> 02:27:54,750 I cannot also have functions called house. 1772 02:27:54,750 --> 02:27:55,916 They're going to collide. 1773 02:27:55,916 --> 02:27:57,041 You've got to decide. 1774 02:27:57,041 --> 02:27:59,250 Do you want the variable to be called house? 1775 02:27:59,250 --> 02:28:01,708 Or do you want the function to be called house? 1776 02:28:01,708 --> 02:28:04,208 Unfortunately, you can't have both because now Python 1777 02:28:04,208 --> 02:28:06,041 is going to confuse one for the other. 1778 02:28:06,041 --> 02:28:09,291 So the conventional fix for this is to do this-- 1779 02:28:09,291 --> 02:28:15,250 to have the setter not store the value that's passed in self.house, 1780 02:28:15,250 --> 02:28:19,125 but to use an almost identical name, but to use a little indicator that 1781 02:28:19,125 --> 02:28:21,208 means you know doing this correctly. 1782 02:28:21,208 --> 02:28:23,666 You typically, by convention, put an underscore 1783 02:28:23,666 --> 02:28:27,041 in front of the instance variable's name. 1784 02:28:27,041 --> 02:28:31,208 And when you return it up here, you similarly put an underscore. 1785 02:28:31,208 --> 02:28:36,541 So now, technically, my instance variable is called _house, 1786 02:28:36,541 --> 02:28:41,791 but my property, which is a fancier attribute, if you will, 1787 02:28:41,791 --> 02:28:44,916 is called house alone. 1788 02:28:44,916 --> 02:28:48,666 Huge amount of syntax, I know, but it's a very powerful feature. 1789 02:28:48,666 --> 02:28:52,708 And again, this is why you can graduate from dictionaries alone and have 1790 02:28:52,708 --> 02:28:55,500 so much more functionality at your disposal. 1791 02:28:55,500 --> 02:28:58,916 Let me go ahead and clear my terminal window and run Python of student.py, 1792 02:28:58,916 --> 02:29:00,458 Enter, name. 1793 02:29:00,458 --> 02:29:02,708 All right, let's go ahead and type in Harry. 1794 02:29:02,708 --> 02:29:04,625 Let's go ahead and type in Gryffindor. 1795 02:29:04,625 --> 02:29:06,416 Crossing my fingers as always. 1796 02:29:06,416 --> 02:29:09,083 And now, look, "Invalid house." 1797 02:29:09,083 --> 02:29:10,250 This is a good thing. 1798 02:29:10,250 --> 02:29:10,750 Why? 1799 02:29:10,750 --> 02:29:13,958 Because, notice, in my main function, I'm 1800 02:29:13,958 --> 02:29:19,125 still trying, maliciously, if you will, to change Harry's house 1801 02:29:19,125 --> 02:29:21,000 to not be one of the four valid ones. 1802 02:29:21,000 --> 02:29:24,833 I'm trying to change it to his childhood home of Number Four, Privet Drive. 1803 02:29:24,833 --> 02:29:29,208 But because Python knows that, wait a minute, you're trying to assign-- 1804 02:29:29,208 --> 02:29:30,708 that is, set a value-- 1805 02:29:30,708 --> 02:29:34,208 and that value, a.k.a. house, is now defined 1806 02:29:34,208 --> 02:29:38,333 as a property you're going to have to go through the setter function instead 1807 02:29:38,333 --> 02:29:40,958 to even let you change that value. 1808 02:29:40,958 --> 02:29:44,208 And because I have this raise ValueError. 1809 02:29:44,208 --> 02:29:46,916 If the house is not as intended, you're not 1810 02:29:46,916 --> 02:29:49,458 going to be allowed to change it to an invalid value. 1811 02:29:49,458 --> 02:29:52,458 So I'm protecting the data on the way in, through the init method, 1812 02:29:52,458 --> 02:29:56,708 and I'm even defending the data if you try to override it there. 1813 02:29:56,708 --> 02:29:59,208 So I think the only solution for me, the programmer, 1814 02:29:59,208 --> 02:30:01,250 is, don't try to break my own code. 1815 02:30:01,250 --> 02:30:04,083 Let me remove that line because it's just not going to work. 1816 02:30:04,083 --> 02:30:07,250 Let me run Python of student.py and, again, type in Harry; 1817 02:30:07,250 --> 02:30:10,916 type in Gryffindor, Enter, and Harry's indeed from Gryffindor. 1818 02:30:10,916 --> 02:30:15,708 If I did something incorrect, like Harry from Number Four, 1819 02:30:15,708 --> 02:30:20,541 Privet Drive, Enter, we're again going to see the value error 1820 02:30:20,541 --> 02:30:25,083 because my code just doesn't let that value in via manual input now 1821 02:30:25,083 --> 02:30:28,541 or via that adversarial change. 1822 02:30:28,541 --> 02:30:29,958 All right, that was a lot. 1823 02:30:29,958 --> 02:30:35,208 But any question on properties? 1824 02:30:35,208 --> 02:30:37,125 AUDIENCE: Why we are using getter then setter? 1825 02:30:37,125 --> 02:30:41,541 It's just for the purpose so that we can find 1826 02:30:41,541 --> 02:30:43,791 that method, that function in our code. 1827 02:30:43,791 --> 02:30:46,833 DAVID J. MALAN: The reason that I'm going through the trouble of defining 1828 02:30:46,833 --> 02:30:52,375 this getter or setter is because I want to make sure that programmers cannot do 1829 02:30:52,375 --> 02:30:53,250 things like this. 1830 02:30:53,250 --> 02:30:55,500 If I'm going through the trouble of validating 1831 02:30:55,500 --> 02:30:58,708 the attributes for these student objects, 1832 02:30:58,708 --> 02:31:02,041 I don't want you to be able to go in there and just change them at will. 1833 02:31:02,041 --> 02:31:04,625 I want to have some control over that object 1834 02:31:04,625 --> 02:31:07,708 so that you can just trust that it's going to be correct as designed. 1835 02:31:07,708 --> 02:31:11,041 So using a getter and setter really just enables 1836 02:31:11,041 --> 02:31:15,416 Python to automatically detect when you're trying to manually set a value. 1837 02:31:15,416 --> 02:31:18,333 The equal sign and the dot, as I've highlighted here, 1838 02:31:18,333 --> 02:31:20,666 is enough of a clue to Python to realize, wait a minute, 1839 02:31:20,666 --> 02:31:21,916 you're trying to set a value. 1840 02:31:21,916 --> 02:31:24,750 Let me see if this class has a setter defined. 1841 02:31:24,750 --> 02:31:26,875 And if so, I'm going to call that, and I'm not just 1842 02:31:26,875 --> 02:31:30,125 going to blindly assign the value from right to left. 1843 02:31:30,125 --> 02:31:32,541 So it's just giving me more control. 1844 02:31:32,541 --> 02:31:35,041 Other questions on properties. 1845 02:31:35,041 --> 02:31:39,458 AUDIENCE: When we use getters, we just have just one argument. 1846 02:31:39,458 --> 02:31:43,291 And if we use setters, it's always going to be two arguments? 1847 02:31:43,291 --> 02:31:44,291 Is that normal? 1848 02:31:44,291 --> 02:31:45,291 DAVID J. MALAN: Correct. 1849 02:31:45,291 --> 02:31:47,833 It's always going to be one argument-- self 1850 02:31:47,833 --> 02:31:52,625 for the getter, two arguments for the setter-- self and something else. 1851 02:31:52,625 --> 02:31:55,375 And the intuition for that is, if you're getting a value, 1852 02:31:55,375 --> 02:31:58,875 you don't need to pass anything else in because you already know the object. 1853 02:31:58,875 --> 02:32:00,291 It's called student in this case. 1854 02:32:00,291 --> 02:32:02,791 So you're just going to get the value of that property. 1855 02:32:02,791 --> 02:32:05,458 But if you want to set the property to something else, 1856 02:32:05,458 --> 02:32:07,125 you've got to pass in that argument. 1857 02:32:07,125 --> 02:32:09,625 You've got to pass in the value to which you want to set it. 1858 02:32:09,625 --> 02:32:11,291 So it's always 0 or 1. 1859 02:32:11,291 --> 02:32:17,541 However, you see it as 1 or 2 because, again, any function inside of a class, 1860 02:32:17,541 --> 02:32:18,041 a.k.a. 1861 02:32:18,041 --> 02:32:22,291 a method, is going to be automatically passed self so that you have access 1862 02:32:22,291 --> 02:32:25,208 to that current object in memory. 1863 02:32:25,208 --> 02:32:27,708 How about one other question on properties? 1864 02:32:27,708 --> 02:32:31,791 AUDIENCE: Why didn't we use the same underscore house init method? 1865 02:32:31,791 --> 02:32:33,125 DAVID J. MALAN: A good question. 1866 02:32:33,125 --> 02:32:36,750 So even though I'm using the underscore house here, in my setter, 1867 02:32:36,750 --> 02:32:39,125 and the underscore house here, in my getter, 1868 02:32:39,125 --> 02:32:41,958 I deliberately did not use it up here. 1869 02:32:41,958 --> 02:32:47,333 The reason for that is that, by using self.house and this equal sign, 1870 02:32:47,333 --> 02:32:50,375 that's the same pattern that I want Python to recognize. 1871 02:32:50,375 --> 02:32:53,208 I want Python to automatically call the setter, 1872 02:32:53,208 --> 02:32:56,791 even when I'm passing in the house via the init method. 1873 02:32:56,791 --> 02:33:01,458 If I were to change this to do this, that would circumvent the setter, 1874 02:33:01,458 --> 02:33:05,041 and now there's no error checking in init whatsoever. 1875 02:33:05,041 --> 02:33:06,333 So it's such a fine line. 1876 02:33:06,333 --> 02:33:10,041 The only thing standing between us and error checking or no error checking 1877 02:33:10,041 --> 02:33:12,750 is the presence or absence of this underscore. 1878 02:33:12,750 --> 02:33:14,625 But that's typically the convention. 1879 02:33:14,625 --> 02:33:17,375 By not using the underscore there, make sure 1880 02:33:17,375 --> 02:33:19,708 that even that assignment goes through the setter 1881 02:33:19,708 --> 02:33:22,416 so that, honestly, I, don't have to copy paste the same error 1882 02:33:22,416 --> 02:33:23,583 checking in two places. 1883 02:33:23,583 --> 02:33:25,375 I can put it just in the setter. 1884 02:33:25,375 --> 02:33:28,791 So it's a better design, and that's why I manually retyped it at first, 1885 02:33:28,791 --> 02:33:31,166 but then I deleted it from init. 1886 02:33:31,166 --> 02:33:34,625 Well, allow me to propose that we make one other change to this file. 1887 02:33:34,625 --> 02:33:38,708 Might as well go ahead and define a property for name as well. 1888 02:33:38,708 --> 02:33:41,458 And let me go ahead and do this-- maybe above the house property 1889 02:33:41,458 --> 02:33:45,041 just to keep things in the same order as I defined them earlier. 1890 02:33:45,041 --> 02:33:47,375 Let me give myself another property. 1891 02:33:47,375 --> 02:33:49,125 This one is going to be called name. 1892 02:33:49,125 --> 02:33:51,583 It's going to take one argument called self, as always. 1893 02:33:51,583 --> 02:33:55,625 And this one, very similarly, is just going to return self._name. 1894 02:33:55,625 --> 02:33:57,333 So I'm going to anticipate that I'm going 1895 02:33:57,333 --> 02:34:01,166 to have to rename name also so that I don't have that same collision as 1896 02:34:01,166 --> 02:34:01,875 before. 1897 02:34:01,875 --> 02:34:05,541 But now let me go ahead and define another setter-- 1898 02:34:05,541 --> 02:34:06,750 this one for name. 1899 02:34:06,750 --> 02:34:09,416 So the convention is @name.setter. 1900 02:34:09,416 --> 02:34:10,375 Why name? 1901 02:34:10,375 --> 02:34:13,916 Because the property I just created is called name. 1902 02:34:13,916 --> 02:34:18,833 So the getter and setter work in conjunction in this way, if you will. 1903 02:34:18,833 --> 02:34:22,291 Let me go down under that name setter and define another function, 1904 02:34:22,291 --> 02:34:23,375 also called name. 1905 02:34:23,375 --> 02:34:25,791 But the key thing here is that it's not identical. 1906 02:34:25,791 --> 02:34:30,000 It's not the exact same function name and the exact same number of arguments. 1907 02:34:30,000 --> 02:34:32,500 The setter, again, takes a second argument. 1908 02:34:32,500 --> 02:34:34,250 And I can call it anything I want, but I'm 1909 02:34:34,250 --> 02:34:36,750 going to call it name because that's what's being passed in. 1910 02:34:36,750 --> 02:34:39,041 And I'm going to put my error checking here. 1911 02:34:39,041 --> 02:34:43,541 If not name, just like we used to do, let's go ahead and raise a value error, 1912 02:34:43,541 --> 02:34:48,750 and let's put an explanatory message like "Missing name," quote, unquote. 1913 02:34:48,750 --> 02:34:55,250 Otherwise, let's go ahead and update self._name to equal name. 1914 02:34:55,250 --> 02:34:58,500 And I don't have to change init except to get rid 1915 02:34:58,500 --> 02:35:02,333 of this duplicate error checking now because, again, 1916 02:35:02,333 --> 02:35:07,166 if I use self.name equals here and self.house equals here 1917 02:35:07,166 --> 02:35:09,916 with no underscore, both of those assignments 1918 02:35:09,916 --> 02:35:13,375 are going to go through my two setter functions now. 1919 02:35:13,375 --> 02:35:15,416 Before we run this, let me go ahead and remove 1920 02:35:15,416 --> 02:35:18,791 this adversarial code, which we know won't work because we're catching it. 1921 02:35:18,791 --> 02:35:22,375 Let me go back down to my terminal window and run Python of student.py, 1922 02:35:22,375 --> 02:35:22,916 Enter. 1923 02:35:22,916 --> 02:35:23,958 Let's type in Harry. 1924 02:35:23,958 --> 02:35:25,125 Let's type in Gryffindor. 1925 02:35:25,125 --> 02:35:26,708 And that seems to work. 1926 02:35:26,708 --> 02:35:29,458 Let's try though, again, to run Python of student.py 1927 02:35:29,458 --> 02:35:32,708 with Harry from Number Four, Privet Drive. 1928 02:35:32,708 --> 02:35:34,416 This will not work. 1929 02:35:34,416 --> 02:35:37,291 A value error with invalid house, because that's not 1930 02:35:37,291 --> 02:35:38,791 one of the four Hogwarts houses. 1931 02:35:38,791 --> 02:35:41,041 And now, for good measure, let's run it one more time. 1932 02:35:41,041 --> 02:35:42,583 And let's not even give it a name. 1933 02:35:42,583 --> 02:35:44,375 Let's just hit Enter when prompted. 1934 02:35:44,375 --> 02:35:45,875 I can type anything for the house. 1935 02:35:45,875 --> 02:35:47,958 I'll go ahead and still give it Gryffindor, Enter. 1936 02:35:47,958 --> 02:35:52,958 And now we get another value error, but this one is for missing name. 1937 02:35:52,958 --> 02:35:56,500 So we seem, now, to have all the more of a defense mechanism in place 1938 02:35:56,500 --> 02:35:58,833 to ensure that name is as we expect. 1939 02:35:58,833 --> 02:36:01,000 It's got to have some value that's not blank. 1940 02:36:01,000 --> 02:36:02,500 And house is as we expect. 1941 02:36:02,500 --> 02:36:05,000 It's got to have one of those four values. 1942 02:36:05,000 --> 02:36:08,000 But at the risk of bursting everyone's bubble 1943 02:36:08,000 --> 02:36:11,958 and making you wonder, why did we just go through all of that, 1944 02:36:11,958 --> 02:36:18,875 unfortunately Python really focuses on conventions, not hard constraints. 1945 02:36:18,875 --> 02:36:20,291 And by that, I mean this. 1946 02:36:20,291 --> 02:36:25,208 If I go back into my main function after I've gotten a student on line 30 1947 02:36:25,208 --> 02:36:30,375 and I try to adversarially do something like this-- student.house equals 1948 02:36:30,375 --> 02:36:33,625 "Number Four, Privet Drive," we know this 1949 02:36:33,625 --> 02:36:37,416 won't work because my setter for house is going to catch this. 1950 02:36:37,416 --> 02:36:38,333 Watch again. 1951 02:36:38,333 --> 02:36:40,833 Python of student.py. 1952 02:36:40,833 --> 02:36:42,041 Let's type in Harry. 1953 02:36:42,041 --> 02:36:44,541 Let's type in Gryffindor, which will at least pass 1954 02:36:44,541 --> 02:36:46,958 our check that's induced by init. 1955 02:36:46,958 --> 02:36:52,041 But line 31 is going to trigger the same setter to be called, 1956 02:36:52,041 --> 02:36:55,583 and we're going to raise a value error saying "Invalid house." 1957 02:36:55,583 --> 02:36:58,250 Unfortunately, and if some of you are already 1958 02:36:58,250 --> 02:37:02,375 thinking a little adversarially, tragically, look what you can do. 1959 02:37:02,375 --> 02:37:05,500 You can change .house to be ._house. 1960 02:37:05,500 --> 02:37:06,041 Why? 1961 02:37:06,041 --> 02:37:10,416 Well, the instance variable is now called _house. 1962 02:37:10,416 --> 02:37:13,125 The property is called house, no underscore. 1963 02:37:13,125 --> 02:37:19,041 But the underlying attribute implemented as an instance variable is still called 1964 02:37:19,041 --> 02:37:19,791 _house. 1965 02:37:19,791 --> 02:37:23,541 And tragically, Python of student.py. 1966 02:37:23,541 --> 02:37:25,000 Let's type in Harry. 1967 02:37:25,000 --> 02:37:27,375 Let's type in Gryffindor, which is correct. 1968 02:37:27,375 --> 02:37:28,958 But watch what happens now. 1969 02:37:28,958 --> 02:37:30,333 Oh, my God. 1970 02:37:30,333 --> 02:37:32,000 We slip through. 1971 02:37:32,000 --> 02:37:35,708 So what was the point of all of this emphasis from me 1972 02:37:35,708 --> 02:37:39,541 on doing things the "right way," the Python quick way by having this getter 1973 02:37:39,541 --> 02:37:40,291 and setter? 1974 02:37:40,291 --> 02:37:43,208 Well, unlike languages like Java, that just 1975 02:37:43,208 --> 02:37:46,208 prevent you from doing things like this, Python 1976 02:37:46,208 --> 02:37:49,041 itself allows you to specify that certain instance variables can 1977 02:37:49,041 --> 02:37:52,666 be public and accessible to anyone's code, or protected, 1978 02:37:52,666 --> 02:37:56,791 or private, which means that no one else should be able to change these values. 1979 02:37:56,791 --> 02:38:00,125 In the world of Python, it's just the honor system. 1980 02:38:00,125 --> 02:38:02,416 It's not baked into the language itself that there's 1981 02:38:02,416 --> 02:38:05,500 a notion of visibility, public or private or even somewhere in between 1982 02:38:05,500 --> 02:38:06,250 protected. 1983 02:38:06,250 --> 02:38:08,208 Instead, you're on the honor system. 1984 02:38:08,208 --> 02:38:11,666 And the convention generally is, if an instance 1985 02:38:11,666 --> 02:38:15,750 variable starts with an underscore, please don't touch it. 1986 02:38:15,750 --> 02:38:16,708 Just don't. 1987 02:38:16,708 --> 02:38:19,750 That's on you if you touch that variable and break things. 1988 02:38:19,750 --> 02:38:22,000 The underscore is meant to signify a convention 1989 02:38:22,000 --> 02:38:24,583 that this is meant to be "private," but it really just 1990 02:38:24,583 --> 02:38:25,875 means, please don't touch this. 1991 02:38:25,875 --> 02:38:27,958 Sometimes, if there's two underscores, which you can use, 1992 02:38:27,958 --> 02:38:30,541 too, that's an even greater effort by programmers to say, 1993 02:38:30,541 --> 02:38:32,000 really don't touch this. 1994 02:38:32,000 --> 02:38:35,166 But technically speaking, there's nothing stopping you or me 1995 02:38:35,166 --> 02:38:38,875 from circumventing all of these mechanisms, these properties, 1996 02:38:38,875 --> 02:38:40,125 these getters and setters. 1997 02:38:40,125 --> 02:38:42,250 We're ultimately just on the honor system 1998 02:38:42,250 --> 02:38:46,291 not to do so when we see instance variables prefixed with one, 1999 02:38:46,291 --> 02:38:48,041 or perhaps even two underscores. 2000 02:38:48,041 --> 02:38:50,583 All right, so this is a lot all at once-- 2001 02:38:50,583 --> 02:38:52,875 this Introduction to object-oriented programming. 2002 02:38:52,875 --> 02:38:56,166 But it might come as quite a surprise that, even though we 2003 02:38:56,166 --> 02:39:00,458 might have identified OOP by name in weeks past, 2004 02:39:00,458 --> 02:39:05,458 we've all been using classes and objects for weeks now in this class. 2005 02:39:05,458 --> 02:39:08,791 In fact, if you think back on one of the very first things we did in this class, 2006 02:39:08,791 --> 02:39:12,083 we used integers and just got integers from the user. 2007 02:39:12,083 --> 02:39:13,791 But if you haven't already-- 2008 02:39:13,791 --> 02:39:17,500 if you go and dig into the documentation for integers, 2009 02:39:17,500 --> 02:39:19,916 which, again, lives at this URL here, you 2010 02:39:19,916 --> 02:39:26,166 would actually find that int itself is and has been for weeks a class. 2011 02:39:26,166 --> 02:39:30,541 And in fact, this is the signature of the constructor call for an int, 2012 02:39:30,541 --> 02:39:35,125 whereby you pass in x, like a number, quote, unquote, "50" or, quote, 2013 02:39:35,125 --> 02:39:38,458 unquote, something else-- you pass in optionally the base-- 2014 02:39:38,458 --> 02:39:41,750 10 for decimal, 2 for binary or anything else. 2015 02:39:41,750 --> 02:39:46,208 And that int function will actually return to you, all this time, 2016 02:39:46,208 --> 02:39:49,625 an object of type int. 2017 02:39:49,625 --> 02:39:52,083 That is to say int is a class. 2018 02:39:52,083 --> 02:39:55,250 It is a template, a blueprint for creating integers in memory. 2019 02:39:55,250 --> 02:39:59,125 And any time you and I have converted a string, for, instance to an int, 2020 02:39:59,125 --> 02:40:03,958 you and I have been creating an object of type int that was calling, 2021 02:40:03,958 --> 02:40:07,875 apparently, the underscore underscore, init, underscore underscore method, 2022 02:40:07,875 --> 02:40:09,666 that someone else-- the authors of Python-- 2023 02:40:09,666 --> 02:40:12,500 wrote to give us back that proper integer. 2024 02:40:12,500 --> 02:40:16,541 Besides that, if you can believe it, strs, strings in Python 2025 02:40:16,541 --> 02:40:20,000 have been classes since the first week of this class as well. 2026 02:40:20,000 --> 02:40:22,000 If you look up the documentation for a str, 2027 02:40:22,000 --> 02:40:26,541 which lives at a similar URL there, you will find that, when you instantiate-- 2028 02:40:26,541 --> 02:40:28,083 that is, create a str-- 2029 02:40:28,083 --> 02:40:32,166 it takes, optionally, a parameter called object here, 2030 02:40:32,166 --> 02:40:34,666 the default value of which is just, quote, unquote, 2031 02:40:34,666 --> 02:40:38,000 which allows you to create, in effect, an empty string, a blank string, 2032 02:40:38,000 --> 02:40:38,500 if you will. 2033 02:40:38,500 --> 02:40:42,541 But any time you and I have created strs or even used explicitly 2034 02:40:42,541 --> 02:40:47,333 the str function, you are getting back an object of type str. 2035 02:40:47,333 --> 02:40:52,375 Any time you and I have forced a string to lowercase per the documentation, 2036 02:40:52,375 --> 02:40:58,041 using syntax like this, you and I have been taking an object of type str 2037 02:40:58,041 --> 02:41:03,125 and forcing it all to lowercase by calling a method called lower, 2038 02:41:03,125 --> 02:41:07,958 a method that the authors of Python built into the str class, 2039 02:41:07,958 --> 02:41:11,041 but it's been there from the get-go, so this notion of methods is not 2040 02:41:11,041 --> 02:41:11,833 even new today. 2041 02:41:11,833 --> 02:41:13,750 You wouldn't have been doing it for this long. 2042 02:41:13,750 --> 02:41:17,416 If you've ever called strip to remove the leading and the trailing whitespace 2043 02:41:17,416 --> 02:41:22,166 from a string in Python, you are calling another method that came with Python-- 2044 02:41:22,166 --> 02:41:23,750 written by the authors of Python. 2045 02:41:23,750 --> 02:41:26,000 And even though we didn't call it a class at the time, 2046 02:41:26,000 --> 02:41:29,708 a str, all this time, has been a class. 2047 02:41:29,708 --> 02:41:33,541 And instances of strings are, themselves, objects. 2048 02:41:33,541 --> 02:41:36,291 And those objects come therefore with these functions 2049 02:41:36,291 --> 02:41:39,458 built in-- a.k.a. methods that allow us to do things like force 2050 02:41:39,458 --> 02:41:42,791 to lowercase and strip whitespace from the beginning and end. 2051 02:41:42,791 --> 02:41:43,958 Let's do another. 2052 02:41:43,958 --> 02:41:49,541 list-- any time you've created a list, either syntactically with square 2053 02:41:49,541 --> 02:41:53,541 brackets or literally with L-I-S-T, open parentheses, closed parentheses, 2054 02:41:53,541 --> 02:41:56,375 which is also possible, you have been using a class. 2055 02:41:56,375 --> 02:41:59,833 If you go to the documentation for list, at this similar URL 2056 02:41:59,833 --> 02:42:04,083 here, or more specifically, the tutorial on lists here in Python, 2057 02:42:04,083 --> 02:42:07,208 you will see that a list is and has been, 2058 02:42:07,208 --> 02:42:10,958 since the early weeks of this class, a class itself. 2059 02:42:10,958 --> 02:42:16,166 And that list class takes, as part of its initialization, 2060 02:42:16,166 --> 02:42:21,000 an optional iterable, something that can be iterated over-- like 1, 2, 3, 2061 02:42:21,000 --> 02:42:24,125 or some list of values, and you can then get back 2062 02:42:24,125 --> 02:42:27,416 a list containing those same iterable values. 2063 02:42:27,416 --> 02:42:30,666 If you've ever appended something to a list in this class, 2064 02:42:30,666 --> 02:42:32,583 as I have myself in the past, you've been 2065 02:42:32,583 --> 02:42:37,416 using a method called append that comes with the list class that, per the x 2066 02:42:37,416 --> 02:42:40,541 here, takes an argument that allows you to append something 2067 02:42:40,541 --> 02:42:42,833 to the current list, a.k.a. 2068 02:42:42,833 --> 02:42:45,125 Self in the context of that method. 2069 02:42:45,125 --> 02:42:46,416 We can do this all day long. 2070 02:42:46,416 --> 02:42:49,625 If you've used a dictionary or a dict in Python-- 2071 02:42:49,625 --> 02:42:53,125 I've actually, all this time, been calling them dict objects, 2072 02:42:53,125 --> 02:42:54,500 and that's for a reason. 2073 02:42:54,500 --> 02:42:57,250 dict itself is a class in Python, if you pull up 2074 02:42:57,250 --> 02:42:59,166 its official documentation here. 2075 02:42:59,166 --> 02:43:03,375 And you'll see that it is defined, indeed, as itself a class. 2076 02:43:03,375 --> 02:43:05,333 And that class comes with methods as well. 2077 02:43:05,333 --> 02:43:07,291 And so any time we've manipulated dictionaries, 2078 02:43:07,291 --> 02:43:11,916 we've been underneath the hood, using all of those same methods. 2079 02:43:11,916 --> 02:43:14,208 And in fact, we can see this if we're really curious. 2080 02:43:14,208 --> 02:43:17,375 Let me go back over here to VS Code. 2081 02:43:17,375 --> 02:43:20,250 And let me go ahead and create a new file that, very simply, does 2082 02:43:20,250 --> 02:43:22,500 something play around with data types. 2083 02:43:22,500 --> 02:43:24,833 And let me go ahead and create a new file, for instance, 2084 02:43:24,833 --> 02:43:30,666 called, say, type.py, just so that I can poke around inside of some values. 2085 02:43:30,666 --> 02:43:34,333 And in type.py, I'm just going to go ahead and do this. 2086 02:43:34,333 --> 02:43:38,375 I'm going to print out whatever the type is of, say, the number 50. 2087 02:43:38,375 --> 02:43:41,500 And This is a function you've not necessarily seen me use already, 2088 02:43:41,500 --> 02:43:44,500 and it's not one you would frequently use in your own code. 2089 02:43:44,500 --> 02:43:46,958 There are other ways to detect, if you need to, 2090 02:43:46,958 --> 02:43:48,625 what the type is of a variable. 2091 02:43:48,625 --> 02:43:53,791 But in this case, type of 50 is just going to tell me and then print out 2092 02:43:53,791 --> 02:43:55,833 what the data type is of that value. 2093 02:43:55,833 --> 02:43:58,416 Now, hopefully, all of us could guess that 50 is indeed 2094 02:43:58,416 --> 02:43:59,458 going to be an integer-- 2095 02:43:59,458 --> 02:44:01,666 that is, an int, but we can see it in this way. 2096 02:44:01,666 --> 02:44:04,541 And this, too, is what's powerful about knowing a bit of programming. 2097 02:44:04,541 --> 02:44:06,416 If you want to know the answer to a question, 2098 02:44:06,416 --> 02:44:08,208 just try it out, like I am here. 2099 02:44:08,208 --> 02:44:11,125 So let me go ahead and run Python of type.py, Enter. 2100 02:44:11,125 --> 02:44:12,208 And there it is. 2101 02:44:12,208 --> 02:44:17,458 When you print out the type of the number 50, you'll see on the screen, 2102 02:44:17,458 --> 02:44:20,625 in this cryptic syntax, class 'int.' 2103 02:44:20,625 --> 02:44:23,541 This is not something that you probably want to show to the user. 2104 02:44:23,541 --> 02:44:26,625 But if you yourself just want to poke around and see what's going on 2105 02:44:26,625 --> 02:44:29,166 or maybe use that information somehow, it's 2106 02:44:29,166 --> 02:44:32,833 certainly at your disposal to use this type function for that. 2107 02:44:32,833 --> 02:44:34,583 Let's change it around a little bit. 2108 02:44:34,583 --> 02:44:38,375 Instead of passing as the argument to type 50, as an int, 2109 02:44:38,375 --> 02:44:41,291 let's type something also familiar, like "hello, world," 2110 02:44:41,291 --> 02:44:43,166 in double or single quotes. 2111 02:44:43,166 --> 02:44:45,541 Let me go back to my terminal window, clear the screen, 2112 02:44:45,541 --> 02:44:47,208 and run Python of type.py again. 2113 02:44:47,208 --> 02:44:49,333 And now, voila, there it is. 2114 02:44:49,333 --> 02:44:52,750 All this time, a str is also a class. 2115 02:44:52,750 --> 02:44:54,333 We can do this a few more times, For. 2116 02:44:54,333 --> 02:44:54,833 Instance. 2117 02:44:54,833 --> 02:44:58,875 Let's go ahead and change "hello, world" to just an empty list-- 2118 02:44:58,875 --> 02:45:01,291 open square bracket, closed square bracket. 2119 02:45:01,291 --> 02:45:03,375 And this is starting to look a little cryptic, 2120 02:45:03,375 --> 02:45:04,791 but, again, notice what I'm doing. 2121 02:45:04,791 --> 02:45:06,708 In square brackets is an empty list. 2122 02:45:06,708 --> 02:45:08,083 We've done that before. 2123 02:45:08,083 --> 02:45:11,583 That is the sole argument to this new type function. 2124 02:45:11,583 --> 02:45:14,666 And that's just being passed to the print function 2125 02:45:14,666 --> 02:45:18,458 so that the return value of type is the argument to print. 2126 02:45:18,458 --> 02:45:22,541 So if I now run this code, Python of type.py, there it is. 2127 02:45:22,541 --> 02:45:24,458 A list is a class, too. 2128 02:45:24,458 --> 02:45:28,000 You might recall that I said that you can also create an empty list 2129 02:45:28,000 --> 02:45:31,541 by literally doing list (). 2130 02:45:31,541 --> 02:45:34,375 This is a bit of an inconsistency, as we can now 2131 02:45:34,375 --> 02:45:39,958 identify that int and str and now list-- they're technically all lowercase. 2132 02:45:39,958 --> 02:45:44,708 And I went to great lengths of creating my student class to have that capital 2133 02:45:44,708 --> 02:45:46,583 S. That's a convention. 2134 02:45:46,583 --> 02:45:51,250 Because int and stir and list and others come with Python, 2135 02:45:51,250 --> 02:45:55,416 they decided to make their built-in data types-- even though they're classes-- 2136 02:45:55,416 --> 02:45:56,375 all lowercase. 2137 02:45:56,375 --> 02:45:59,750 But the convention, the recommendation in the Python community when creating 2138 02:45:59,750 --> 02:46:03,541 your classes is to capitalize the first letter, as I did, 2139 02:46:03,541 --> 02:46:10,541 in something like Student, capital S. But list () is identical to really just 2140 02:46:10,541 --> 02:46:12,041 two empty square brackets. 2141 02:46:12,041 --> 02:46:16,416 If I clear my screen and run type.py again, you see the exact same thing. 2142 02:46:16,416 --> 02:46:17,750 The class is called list. 2143 02:46:17,750 --> 02:46:19,041 Let's do one more. 2144 02:46:19,041 --> 02:46:23,041 Let me change the list to be not square brackets but curly braces. 2145 02:46:23,041 --> 02:46:24,125 We've done this before. 2146 02:46:24,125 --> 02:46:27,916 Any time I've done two curly braces with nothing in between, this, of course, 2147 02:46:27,916 --> 02:46:31,458 is an empty dictionary, or a dict object in Python. 2148 02:46:31,458 --> 02:46:32,750 Well, we can see that now. 2149 02:46:32,750 --> 02:46:35,500 Let me clear my screen, run Python of type.py, Enter, 2150 02:46:35,500 --> 02:46:37,666 and there it is-- class 'dict." 2151 02:46:37,666 --> 02:46:39,458 It's been there this whole time. 2152 02:46:39,458 --> 02:46:42,333 We just didn't call it a class until today. 2153 02:46:42,333 --> 02:46:44,625 I can similarly do this one explicitly. 2154 02:46:44,625 --> 02:46:48,500 Instead of two curly braces, let's write out dict with two parentheses. 2155 02:46:48,500 --> 02:46:51,541 Now we have a lot of parentheses again, like with list. 2156 02:46:51,541 --> 02:46:55,250 But this is just making even more clear that the type of a dict object 2157 02:46:55,250 --> 02:46:58,333 is indeed the class, dict, itself. 2158 02:46:58,333 --> 02:47:02,375 So this is to say that, as new as a lot of today's idea and syntax, 2159 02:47:02,375 --> 02:47:05,708 might be you've actually been using it, perhaps unbeknownst to you, 2160 02:47:05,708 --> 02:47:06,750 for weeks now. 2161 02:47:06,750 --> 02:47:09,416 We now just have terminology to describe what 2162 02:47:09,416 --> 02:47:10,958 it is we've been doing all this time. 2163 02:47:10,958 --> 02:47:14,166 And you now have the expressiveness, with some practice, 2164 02:47:14,166 --> 02:47:18,791 to create your own classes, inside of which are your own instance variables, 2165 02:47:18,791 --> 02:47:23,708 perhaps wrapped with those properties and your own instance methods. 2166 02:47:23,708 --> 02:47:27,125 But it turns out there's other types of methods in the world. 2167 02:47:27,125 --> 02:47:29,375 Thus far, I've been deliberate in calling 2168 02:47:29,375 --> 02:47:34,375 all of our variables instance variables and all of our methods instance 2169 02:47:34,375 --> 02:47:35,125 methods. 2170 02:47:35,125 --> 02:47:40,333 It turns out there's other types of variables and methods out there, 2171 02:47:40,333 --> 02:47:42,875 and one of those is called class methods. 2172 02:47:42,875 --> 02:47:48,333 It turns out that sometimes it's not really necessary or sensible 2173 02:47:48,333 --> 02:47:52,333 to associate a function with objects of a class, 2174 02:47:52,333 --> 02:47:55,000 but rather with the class itself. 2175 02:47:55,000 --> 02:47:59,833 An instance, or an object of a class, is a very specific incarnation thereof. 2176 02:47:59,833 --> 02:48:03,333 Again, on that neighborhood that has a lot of identical looking buildings, 2177 02:48:03,333 --> 02:48:06,458 but they're all a little bit different because of different paint and such, 2178 02:48:06,458 --> 02:48:09,666 sometimes you might have functionality related 2179 02:48:09,666 --> 02:48:13,541 to each of those houses that isn't distinct or unique for any 2180 02:48:13,541 --> 02:48:14,333 of the houses. 2181 02:48:14,333 --> 02:48:17,375 It's functionality that's going to be exactly the same no 2182 02:48:17,375 --> 02:48:19,208 matter the house in question. 2183 02:48:19,208 --> 02:48:21,541 Same in the world of object-oriented programming. 2184 02:48:21,541 --> 02:48:24,375 Sometimes you want some functionality, some action 2185 02:48:24,375 --> 02:48:29,500 to be associated with the class itself, no matter what the specific object's 2186 02:48:29,500 --> 02:48:32,333 own values or instance variables are. 2187 02:48:32,333 --> 02:48:36,125 And for that, we have a keyword called @classmethod. 2188 02:48:36,125 --> 02:48:38,458 This is another decorator-- really, another 2189 02:48:38,458 --> 02:48:43,416 function-- that you can use to specify that this method is not, 2190 02:48:43,416 --> 02:48:46,208 by default, implicitly an instance method that 2191 02:48:46,208 --> 02:48:48,791 has access to self, the object itself. 2192 02:48:48,791 --> 02:48:51,750 This is a class method that's not going to have access to self, 2193 02:48:51,750 --> 02:48:54,166 but it does know what class it's inside. 2194 02:48:54,166 --> 02:48:55,625 So what do I mean by this? 2195 02:48:55,625 --> 02:48:57,416 Well, let me go back to VS Code here. 2196 02:48:57,416 --> 02:49:01,291 And let me propose that we create a new file this time implementing 2197 02:49:01,291 --> 02:49:04,958 the notion of a-- the sorting hat, from the world of Harry Potter as well, 2198 02:49:04,958 --> 02:49:06,041 to stay on theme. 2199 02:49:06,041 --> 02:49:08,666 I'm going to go ahead and run code of hat.py. 2200 02:49:08,666 --> 02:49:12,041 And in hat.py, let's implement the notion of the sorting hat. 2201 02:49:12,041 --> 02:49:14,583 If unfamiliar in the books and in the films 2202 02:49:14,583 --> 02:49:17,333 there is literally a pointy hat that, when 2203 02:49:17,333 --> 02:49:20,916 a student put it's on their head, that sorting hat, so to speak, 2204 02:49:20,916 --> 02:49:23,708 decides what house the student is in-- whether it's 2205 02:49:23,708 --> 02:49:25,416 Gryffindor or something else. 2206 02:49:25,416 --> 02:49:29,500 So let's implement, in code, this notion of a sorting hat such that, 2207 02:49:29,500 --> 02:49:33,250 when we pass to the sorting hat the name of a student, like, quote, unquote, 2208 02:49:33,250 --> 02:49:36,083 "Harry" this sorting hat, implemented in code, 2209 02:49:36,083 --> 02:49:40,041 will tell us what house that student should be in. 2210 02:49:40,041 --> 02:49:41,750 Well, let's go ahead and do this. 2211 02:49:41,750 --> 02:49:46,708 In hat.py, first, let's go ahead and define a class called hat, 2212 02:49:46,708 --> 02:49:49,333 and then let's get back to implementing it itself. 2213 02:49:49,333 --> 02:49:52,041 And I find this to be a helpful technique, not just with teaching 2214 02:49:52,041 --> 02:49:53,375 but when writing code. 2215 02:49:53,375 --> 02:49:55,291 I know I want a hat class. 2216 02:49:55,291 --> 02:49:57,541 I don't necessarily know what I want it to do yet, 2217 02:49:57,541 --> 02:50:00,083 so I'm going to create this placeholder, dot, dot, dot, 2218 02:50:00,083 --> 02:50:01,791 so I'll come back to that. 2219 02:50:01,791 --> 02:50:05,000 Let's now try to use this class as though it existed. 2220 02:50:05,000 --> 02:50:08,250 And from there, I perhaps can realize exactly what 2221 02:50:08,250 --> 02:50:11,625 functionality that class needs to have to support my use case. 2222 02:50:11,625 --> 02:50:14,708 Let me go ahead and create a variable called hat in all lowercase 2223 02:50:14,708 --> 02:50:17,500 and instantiate a hat object. 2224 02:50:17,500 --> 02:50:21,041 So no matter what the hat class ends up looking like, 2225 02:50:21,041 --> 02:50:24,125 this is the common syntax for instantiating 2226 02:50:24,125 --> 02:50:25,916 an object of a certain class. 2227 02:50:25,916 --> 02:50:28,958 In the past, we saw student, all lowercase, 2228 02:50:28,958 --> 02:50:32,708 equals capital Student, open parenthesis, close parentheses, 2229 02:50:32,708 --> 02:50:35,375 and then eventually, we added in things like name and house. 2230 02:50:35,375 --> 02:50:39,375 For now, let's assume that the hat is much simpler than a student, 2231 02:50:39,375 --> 02:50:41,500 and it only has sorting capabilities. 2232 02:50:41,500 --> 02:50:44,791 So I'm not going to even pass any arguments there, too. 2233 02:50:44,791 --> 02:50:50,708 Let me assume that the sorting hat has one function-- one method inside of it 2234 02:50:50,708 --> 02:50:51,583 called, sort. 2235 02:50:51,583 --> 02:50:59,666 And so if I do hat.sort ("Harry"), let's propose that that prints out what house 2236 02:50:59,666 --> 02:51:01,458 that student should be in. 2237 02:51:01,458 --> 02:51:02,541 So that's it. 2238 02:51:02,541 --> 02:51:04,541 I'm going to encapsulate-- 2239 02:51:04,541 --> 02:51:06,916 that is tuck away inside of a hat class-- 2240 02:51:06,916 --> 02:51:11,250 all of this requisite functionality, and I'm going to print out onto the screen 2241 02:51:11,250 --> 02:51:12,458 what hat-- 2242 02:51:12,458 --> 02:51:15,208 what house Harry belongs in. 2243 02:51:15,208 --> 02:51:20,166 Now I think I need to get into the weeds of actually initializing this class. 2244 02:51:20,166 --> 02:51:22,041 Well, let me go ahead and do this. 2245 02:51:22,041 --> 02:51:24,791 If I don't care to parameterize hat-- 2246 02:51:24,791 --> 02:51:29,083 I just want to, for instance sort values, 2247 02:51:29,083 --> 02:51:31,916 let's go ahead and define this function, sort, first. 2248 02:51:31,916 --> 02:51:35,291 So let's define sort, as taking a first argument, self, 2249 02:51:35,291 --> 02:51:39,125 which is always going to be the case when defining an instance method as 2250 02:51:39,125 --> 02:51:39,750 before. 2251 02:51:39,750 --> 02:51:43,750 But the sort method clearly takes one argument from the programmer, me-- 2252 02:51:43,750 --> 02:51:45,583 namely the student's name. 2253 02:51:45,583 --> 02:51:47,708 And again, we've seen this dichotomy before. 2254 02:51:47,708 --> 02:51:52,083 Even though I'm trying to pass in one argument, when I define the method, 2255 02:51:52,083 --> 02:51:55,166 it's got to take that many arguments, plus one more-- 2256 02:51:55,166 --> 02:51:58,208 self which is always going to be automatically passed in by Python 2257 02:51:58,208 --> 02:51:59,583 first. 2258 02:51:59,583 --> 02:52:01,291 What do I want to do? 2259 02:52:01,291 --> 02:52:03,291 Well, let's go ahead and do something like this. 2260 02:52:03,291 --> 02:52:06,166 2261 02:52:06,166 --> 02:52:12,375 Print, this name-- how about "is in," "some house." 2262 02:52:12,375 --> 02:52:14,291 I'm going to, again, use some placeholder code 2263 02:52:14,291 --> 02:52:18,000 for myself because I'm not quite sure how to finish implementing this sorting 2264 02:52:18,000 --> 02:52:18,583 hat. 2265 02:52:18,583 --> 02:52:21,708 But I think that's enough to just test where my code is at now. 2266 02:52:21,708 --> 02:52:25,625 Let me go ahead and run Python of hat.py and hit Enter. 2267 02:52:25,625 --> 02:52:28,250 And it looks like, indeed, Harry is in some house. 2268 02:52:28,250 --> 02:52:31,958 We're not done yet because it's clearly not doing anything interesting, 2269 02:52:31,958 --> 02:52:35,791 but it at least is running correctly with no errors. 2270 02:52:35,791 --> 02:52:40,125 Well, let's go ahead now and decide where-- 2271 02:52:40,125 --> 02:52:43,541 what house Harry should actually be in by introducing a bit of randomness 2272 02:52:43,541 --> 02:52:45,625 and choosing a house randomly. 2273 02:52:45,625 --> 02:52:47,500 While I can do this in a few ways, Let. 2274 02:52:47,500 --> 02:52:48,875 Me go ahead and do this. 2275 02:52:48,875 --> 02:52:51,541 I need to have a list of houses somewhere. 2276 02:52:51,541 --> 02:52:52,833 So where can I put that? 2277 02:52:52,833 --> 02:52:54,708 I could solve this problem in different ways. 2278 02:52:54,708 --> 02:52:56,000 Let me propose that I do this. 2279 02:52:56,000 --> 02:52:58,833 Let me define a method called init, as I've done before, 2280 02:52:58,833 --> 02:53:01,250 that takes in self, but no other arguments. 2281 02:53:01,250 --> 02:53:04,583 And whenever the sorting hat is instantiated, let's do this. 2282 02:53:04,583 --> 02:53:09,958 Let's create a houses instance variable, plural, that equals this list-- 2283 02:53:09,958 --> 02:53:16,958 Gryffindor, Hufflepuff, Ravenclaw, Slytherin, 2284 02:53:16,958 --> 02:53:19,416 so the exact same list that we've used before, 2285 02:53:19,416 --> 02:53:23,333 and I'm storing it in an instance variable inside of this class. 2286 02:53:23,333 --> 02:53:26,708 I'm not taking any arguments beyond self to init, 2287 02:53:26,708 --> 02:53:30,625 but I just need this list of values somewhere, for instance. 2288 02:53:30,625 --> 02:53:32,250 So what can I do here? 2289 02:53:32,250 --> 02:53:36,416 Well, let me go ahead and replace some house with the actual house. 2290 02:53:36,416 --> 02:53:37,541 Well, what could I do here? 2291 02:53:37,541 --> 02:53:39,583 Well, I want to put a house there. 2292 02:53:39,583 --> 02:53:42,083 Well, let's go ahead and create a variable called house. 2293 02:53:42,083 --> 02:53:46,291 And if you think back to our discussion of libraries, in the random module, 2294 02:53:46,291 --> 02:53:51,791 there is a function called choice that, if you pass in a list of choices, 2295 02:53:51,791 --> 02:53:56,333 like self.houses, that will pick a random house out of those four. 2296 02:53:56,333 --> 02:53:58,750 And then on line 7, I can pass it in. 2297 02:53:58,750 --> 02:54:01,916 If I want to tighten this up, let me just go ahead and highlight that code, 2298 02:54:01,916 --> 02:54:03,125 get rid of the variable. 2299 02:54:03,125 --> 02:54:04,541 It's technically unnecessary. 2300 02:54:04,541 --> 02:54:06,666 And because the line of code is still pretty short, 2301 02:54:06,666 --> 02:54:09,458 I'm OK with just putting it all in one line. 2302 02:54:09,458 --> 02:54:13,000 But I could certainly use the variable like I did a moment ago. 2303 02:54:13,000 --> 02:54:14,041 So what have I done? 2304 02:54:14,041 --> 02:54:20,708 In my init function, I have defined a initialization of the object 2305 02:54:20,708 --> 02:54:23,875 that stores in self.houses the list of four houses. 2306 02:54:23,875 --> 02:54:26,583 And then, in sort, I'm accessing that same list, 2307 02:54:26,583 --> 02:54:30,041 but I'm randomly choosing the set of houses there. 2308 02:54:30,041 --> 02:54:31,791 Now, why have I done it in this way? 2309 02:54:31,791 --> 02:54:33,750 This, too, is general convention. 2310 02:54:33,750 --> 02:54:35,916 Any time you have a list of things that-- who knows? 2311 02:54:35,916 --> 02:54:38,000 Maybe will change over time. 2312 02:54:38,000 --> 02:54:41,541 Places like Harvard have constructed new houses over the years, 2313 02:54:41,541 --> 02:54:43,958 so you might have to change the list of available houses. 2314 02:54:43,958 --> 02:54:47,083 It didn't happen in seven books or eight films of Harry Potter. 2315 02:54:47,083 --> 02:54:49,208 But you could imagine maybe Hogwarts eventually 2316 02:54:49,208 --> 02:54:54,291 has a fifth house, so there's generally some value in putting list of constants 2317 02:54:54,291 --> 02:54:57,333 toward the top of your file, toward the top of the class so it's just 2318 02:54:57,333 --> 02:54:59,875 obvious what the list of values is. 2319 02:54:59,875 --> 02:55:02,750 You don't want to necessarily tuck it away in some function, 2320 02:55:02,750 --> 02:55:06,375 like sort, especially if you might want to use that function-- 2321 02:55:06,375 --> 02:55:10,166 sorry, especially if you want to use that list in multiple functions, not 2322 02:55:10,166 --> 02:55:10,708 just sort. 2323 02:55:10,708 --> 02:55:12,583 But if I kept adding to this class, you might 2324 02:55:12,583 --> 02:55:15,083 want to use that same list of houses in multiple functions. 2325 02:55:15,083 --> 02:55:20,541 So let's keep it in the object itself by storing it in self.houses. 2326 02:55:20,541 --> 02:55:23,625 All right, well, we're about to change the course of history here perhaps. 2327 02:55:23,625 --> 02:55:27,666 Let me do Python of hat.py, and I think we're about to assign Harry 2328 02:55:27,666 --> 02:55:30,833 to one of those four houses randomly. 2329 02:55:30,833 --> 02:55:33,041 Huh, NameError. 2330 02:55:33,041 --> 02:55:35,041 Name 'random' is not defined. 2331 02:55:35,041 --> 02:55:38,416 Well, wait a minute, where did I go wrong here? 2332 02:55:38,416 --> 02:55:43,541 Thinking back to our class on libraries, why did my code break and not tell me 2333 02:55:43,541 --> 02:55:45,625 where Harry is to be? 2334 02:55:45,625 --> 02:55:48,708 AUDIENCE: You did not import the random library. 2335 02:55:48,708 --> 02:55:49,708 DAVID J. MALAN: Exactly. 2336 02:55:49,708 --> 02:55:52,541 If the random library or module is something I want to use, 2337 02:55:52,541 --> 02:55:55,583 I need to tell Python that at the top of my file. 2338 02:55:55,583 --> 02:55:58,958 So let me go up here and do import random. 2339 02:55:58,958 --> 02:56:01,708 And then, below that, let me go ahead and clear my terminal window 2340 02:56:01,708 --> 02:56:02,416 and try again. 2341 02:56:02,416 --> 02:56:08,041 Python of hat.py, crossing my fingers, seeing where Harry is going to end up. 2342 02:56:08,041 --> 02:56:12,041 And, OK, Harry as of now is officially in Hufflepuff, 2343 02:56:12,041 --> 02:56:14,500 despite everything you've read or seen. 2344 02:56:14,500 --> 02:56:15,875 Well, let's run this again. 2345 02:56:15,875 --> 02:56:19,833 Let me clear my window and run Python of hat.py, and now he's in Ravenclaw. 2346 02:56:19,833 --> 02:56:21,541 That's consistent with using random. 2347 02:56:21,541 --> 02:56:23,375 Let's clear that and run it again. 2348 02:56:23,375 --> 02:56:25,458 He's still in Ravenclaw, but that could happen, 2349 02:56:25,458 --> 02:56:26,833 even though there's four choices. 2350 02:56:26,833 --> 02:56:27,625 Let's do it again. 2351 02:56:27,625 --> 02:56:29,250 Hufflepuff-- back in Hufflepuff. 2352 02:56:29,250 --> 02:56:31,666 We can't seem to get the right answer. 2353 02:56:31,666 --> 02:56:34,375 Now he's in Gryffindor, albeit randomly. 2354 02:56:34,375 --> 02:56:37,208 So we seem to have a program that, based on these limited tests, 2355 02:56:37,208 --> 02:56:41,166 seems to be assigning Harry to a house randomly. 2356 02:56:41,166 --> 02:56:45,416 Now I'm somewhat lazily just letting sort print out this value. 2357 02:56:45,416 --> 02:56:50,375 I could do something else, like return a string, and then let me, on line 13, 2358 02:56:50,375 --> 02:56:51,916 do the printing for me. 2359 02:56:51,916 --> 02:56:57,000 But for now, I think we have an example of a class called hat that, 2360 02:56:57,000 --> 02:57:00,125 nonetheless, applies some of our lessons learned thus far today, 2361 02:57:00,125 --> 02:57:03,333 where I've created a class-- because a sorting hat is, frankly-- 2362 02:57:03,333 --> 02:57:06,250 well, I was about to say real world entity, but really 2363 02:57:06,250 --> 02:57:07,875 a fantasy world entity. 2364 02:57:07,875 --> 02:57:11,791 And indeed, that's a, perhaps, common heuristic or mental model to have. 2365 02:57:11,791 --> 02:57:16,041 When should you use a class to represent something in your code? 2366 02:57:16,041 --> 02:57:20,625 Very often, when you're trying to represent some real world entity 2367 02:57:20,625 --> 02:57:25,791 or fantasy world entity, like a student, which is something in the real world, 2368 02:57:25,791 --> 02:57:28,625 like a sorting hat, which, OK, doesn't exist, 2369 02:57:28,625 --> 02:57:32,166 but hat's certainly do, so quite reasonable to have a class for hat. 2370 02:57:32,166 --> 02:57:36,500 And that's not always the case that classes represent real world entities. 2371 02:57:36,500 --> 02:57:42,291 But we've seen thus far that int and stir and list and dict-- these 2372 02:57:42,291 --> 02:57:44,666 are all structures that you might have in the real world. 2373 02:57:44,666 --> 02:57:47,416 We have integers and strings of text and other things. 2374 02:57:47,416 --> 02:57:50,833 So it rather makes sense to represent even those things, more technically, 2375 02:57:50,833 --> 02:57:52,625 using a class as well. 2376 02:57:52,625 --> 02:57:56,291 You could use just a dictionary to represent a student or a hat. 2377 02:57:56,291 --> 02:58:00,875 But again, with classes come all this and even more functionality. 2378 02:58:00,875 --> 02:58:06,791 But I honestly am not using classes in, really, the "right way" here. 2379 02:58:06,791 --> 02:58:07,416 Why? 2380 02:58:07,416 --> 02:58:10,625 Well, in the world of Harry Potter there really is only, 2381 02:58:10,625 --> 02:58:12,791 to my knowledge, one sorting hat. 2382 02:58:12,791 --> 02:58:17,000 And yet, here I have gone and implemented a class called hat. 2383 02:58:17,000 --> 02:58:20,333 And again, a class is like a blueprint, a template, 2384 02:58:20,333 --> 02:58:24,625 a mold that allows you to create one or more objects thereof. 2385 02:58:24,625 --> 02:58:27,041 Now, most of my programs Thus far have been pretty simple, 2386 02:58:27,041 --> 02:58:28,750 and I've just created one student. 2387 02:58:28,750 --> 02:58:31,208 But certainly, if I spent more time and wrote more code, 2388 02:58:31,208 --> 02:58:33,583 you could imagine writing one program that 2389 02:58:33,583 --> 02:58:36,208 has a list of students-- many more students 2390 02:58:36,208 --> 02:58:38,708 than just the one we keep demonstrating. 2391 02:58:38,708 --> 02:58:40,625 Yet it would be a little weird-- 2392 02:58:40,625 --> 02:58:43,291 it's a little inconsistent with the real or the fantasy 2393 02:58:43,291 --> 02:58:48,041 world of Harry Potter to instantiate one, two, three or more sorting hats. 2394 02:58:48,041 --> 02:58:49,708 There really is just one. 2395 02:58:49,708 --> 02:58:51,875 Really one singleton, if you will, which is 2396 02:58:51,875 --> 02:58:54,458 a term of art in a lot of contexts of programming. 2397 02:58:54,458 --> 02:58:58,125 So let me propose that we actually improve the design of the sorting hat 2398 02:58:58,125 --> 02:59:03,083 so that we don't have to instantiate a sorting hat because right now this 2399 02:59:03,083 --> 02:59:08,791 is kind of allowing me to do something like hat 1 = hat, hat 2 = hat, hat 3 =, 2400 02:59:08,791 --> 02:59:09,458 and so forth. 2401 02:59:09,458 --> 02:59:11,291 I don't really need that capability. 2402 02:59:11,291 --> 02:59:15,250 I really just need to represent the sorting hat with a class, 2403 02:59:15,250 --> 02:59:17,125 but I don't really need to instantiate it. 2404 02:59:17,125 --> 02:59:17,625 Why? 2405 02:59:17,625 --> 02:59:18,708 Because it already exists. 2406 02:59:18,708 --> 02:59:19,625 I need just one. 2407 02:59:19,625 --> 02:59:23,041 So it turns out, in Python, that, up until now, 2408 02:59:23,041 --> 02:59:25,833 we've been using, as I keep calling them, instance methods-- 2409 02:59:25,833 --> 02:59:30,416 writing functions inside of classes that are automatically passed a reference 2410 02:59:30,416 --> 02:59:32,375 to self, the current object. 2411 02:59:32,375 --> 02:59:34,375 But sometimes you just don't need that. 2412 02:59:34,375 --> 02:59:37,083 Sometimes it suffices to just know what the class is 2413 02:59:37,083 --> 02:59:40,875 and assume that there might not even be any objects of that class. 2414 02:59:40,875 --> 02:59:45,666 So in this sense, you can use a class really as a container for data 2415 02:59:45,666 --> 02:59:50,291 and/or functionality that is just somehow conceptually related-- 2416 02:59:50,291 --> 02:59:52,791 things related to a sorting hat. 2417 02:59:52,791 --> 02:59:56,083 And there's this other decorator or function called @classmethod 2418 02:59:56,083 --> 02:59:57,750 that allows us to do just this. 2419 02:59:57,750 --> 02:59:59,375 So let me go back to my code here. 2420 02:59:59,375 --> 03:00:05,041 And let me propose that, if I'm not going to instantiate multiple houses, 2421 03:00:05,041 --> 03:00:07,916 I don't really need this init method because that's really 2422 03:00:07,916 --> 03:00:12,250 meant to initialize specific objects from that blueprint, that template, 2423 03:00:12,250 --> 03:00:13,083 that mold. 2424 03:00:13,083 --> 03:00:14,708 So let me get rid of this. 2425 03:00:14,708 --> 03:00:17,791 But if I get rid of this, I no longer have access to self. 2426 03:00:17,791 --> 03:00:22,750 But that's OK because it turns out, in addition to their existing class 2427 03:00:22,750 --> 03:00:26,416 methods, there are also what we might call class variables. 2428 03:00:26,416 --> 03:00:30,791 And class variables exist within the class itself. 2429 03:00:30,791 --> 03:00:33,833 And there's just one copy of that variable 2430 03:00:33,833 --> 03:00:35,958 for all of the objects thereof. 2431 03:00:35,958 --> 03:00:40,291 They all share, if you will, the same variable-- be it an int or str 2432 03:00:40,291 --> 03:00:41,750 or, in this case, a list. 2433 03:00:41,750 --> 03:00:49,041 So what I've done here is define, inside of my hat class, in a class variable 2434 03:00:49,041 --> 03:00:50,208 called houses-- 2435 03:00:50,208 --> 03:00:53,083 I don't say self because self is no longer relevant. 2436 03:00:53,083 --> 03:00:54,791 Self refers to specific objects. 2437 03:00:54,791 --> 03:00:58,166 I want a variable inside of this class, a.k.a. 2438 03:00:58,166 --> 03:01:00,791 A class variable that equals that list. 2439 03:01:00,791 --> 03:01:03,708 Because it's inside of this hat, now, class, 2440 03:01:03,708 --> 03:01:07,208 I can use that list in any of my functions. 2441 03:01:07,208 --> 03:01:08,958 I've only got one now, called sort. 2442 03:01:08,958 --> 03:01:11,791 But if I had more, it would be accessible to all of those methods 2443 03:01:11,791 --> 03:01:12,666 as well. 2444 03:01:12,666 --> 03:01:15,791 And with sort, it also doesn't really make sense 2445 03:01:15,791 --> 03:01:19,250 to sort within a specific sorting hat because, again, I 2446 03:01:19,250 --> 03:01:20,500 only want there to be one. 2447 03:01:20,500 --> 03:01:25,875 So I can actually specify that this is class method by saying @classmethod. 2448 03:01:25,875 --> 03:01:28,083 And I don't pass in self anymore. 2449 03:01:28,083 --> 03:01:33,250 I actually, by convention, pass in a reference to the class itself. 2450 03:01:33,250 --> 03:01:35,250 It's typically written as cls. 2451 03:01:35,250 --> 03:01:35,791 Why? 2452 03:01:35,791 --> 03:01:40,916 Well, if you wrote C-L-A-S-S, that would actually conflict with the keyword 2453 03:01:40,916 --> 03:01:42,791 "class" that we keep using up here. 2454 03:01:42,791 --> 03:01:47,125 So the world realized that, oops, we can't reuse that same phrase here. 2455 03:01:47,125 --> 03:01:48,833 So let's just call this class. 2456 03:01:48,833 --> 03:01:51,541 This is useful in some contexts including this one. 2457 03:01:51,541 --> 03:01:52,041 Why? 2458 03:01:52,041 --> 03:01:54,125 Well, notice what I can now do. 2459 03:01:54,125 --> 03:01:56,916 I can now change self to be just class. 2460 03:01:56,916 --> 03:01:57,541 Why? 2461 03:01:57,541 --> 03:02:02,708 Because houses now-- not an instance variable, accessible via self.houses. 2462 03:02:02,708 --> 03:02:07,041 It is now a class variable, accessible via class.houses, 2463 03:02:07,041 --> 03:02:10,375 or technically cls.houses in this case. 2464 03:02:10,375 --> 03:02:12,666 But now the final flourish is this. 2465 03:02:12,666 --> 03:02:19,208 Now, I don't have to instantiate any hat objects as I used to on here, line 13. 2466 03:02:19,208 --> 03:02:22,833 I can just use functionality that comes with this class. 2467 03:02:22,833 --> 03:02:24,791 So I'm going to delete that line altogether. 2468 03:02:24,791 --> 03:02:32,416 I'm going to capitalize the hat on this new line 13 and just say hat.sort, 2469 03:02:32,416 --> 03:02:33,333 ("Harry"). 2470 03:02:33,333 --> 03:02:34,750 So what have I done? 2471 03:02:34,750 --> 03:02:38,541 I've not bothered instantiating an object of type, hat. 2472 03:02:38,541 --> 03:02:42,791 I am just accessing a class method inside of the hat class 2473 03:02:42,791 --> 03:02:43,666 that-- you know what? 2474 03:02:43,666 --> 03:02:45,208 Is just going to work. 2475 03:02:45,208 --> 03:02:46,708 This is how class methods work. 2476 03:02:46,708 --> 03:02:51,041 You use the name of the class, capital letter and all, dot method name, 2477 03:02:51,041 --> 03:02:53,041 passing in any arguments you want. 2478 03:02:53,041 --> 03:02:57,000 Python is going to automatically pass in some variable via which 2479 03:02:57,000 --> 03:03:00,625 you can refer to that class in that function 2480 03:03:00,625 --> 03:03:02,458 that you've implemented inside of that class 2481 03:03:02,458 --> 03:03:04,125 so that I can do something like this. 2482 03:03:04,125 --> 03:03:07,833 It's not that I want a variable called houses locally in this function, 2483 03:03:07,833 --> 03:03:12,541 I want the variable called houses that's associated with this current class 2484 03:03:12,541 --> 03:03:17,166 so I can still access this same list that I defined on line 6. 2485 03:03:17,166 --> 03:03:21,500 And now, if I go back down here to my terminal and run Python of hat.py, 2486 03:03:21,500 --> 03:03:24,875 Enter, Harry is still in Hufflepuff once more. 2487 03:03:24,875 --> 03:03:27,041 Harry is still in Hufflepuff once more. 2488 03:03:27,041 --> 03:03:31,250 Harry is back in Gryffindor, at least randomly. 2489 03:03:31,250 --> 03:03:36,333 Questions, now, on these class variables or these class methods, 2490 03:03:36,333 --> 03:03:41,416 which are in contrast with instance variables and instance methods. 2491 03:03:41,416 --> 03:03:43,875 And the one thing, at least, that's a little strange 2492 03:03:43,875 --> 03:03:47,666 here is that, even though there's a decorator called @classmethod, 2493 03:03:47,666 --> 03:03:51,291 there is not one called @instancemethod. 2494 03:03:51,291 --> 03:03:55,666 A method is just automatically a so-called "instant method" when 2495 03:03:55,666 --> 03:03:57,541 you define it without any decorator. 2496 03:03:57,541 --> 03:04:00,291 AUDIENCE: Can you have a class inside another class? 2497 03:04:00,291 --> 03:04:01,291 DAVID J. MALAN: You can. 2498 03:04:01,291 --> 03:04:03,666 You can define one class inside of another. 2499 03:04:03,666 --> 03:04:06,041 Generally speaking, this isn't done, but there 2500 03:04:06,041 --> 03:04:08,416 are cases where it can be helpful, especially 2501 03:04:08,416 --> 03:04:10,625 for larger, more sophisticated programs. 2502 03:04:10,625 --> 03:04:13,541 So yes, it is possible. 2503 03:04:13,541 --> 03:04:14,750 Other questions. 2504 03:04:14,750 --> 03:04:17,958 AUDIENCE: The question was about the self.houses. 2505 03:04:17,958 --> 03:04:25,083 When we remove it and we pass data, variable is created itself, 2506 03:04:25,083 --> 03:04:27,458 s why we remove the self? 2507 03:04:27,458 --> 03:04:29,625 DAVID J. MALAN: So in the previous examples-- 2508 03:04:29,625 --> 03:04:32,625 both of the hat demonstration and also all of the student 2509 03:04:32,625 --> 03:04:36,708 demonstrations-- we were creating a student 2510 03:04:36,708 --> 03:04:40,625 object by calling Student, capital S, open parenthesis, close parenthesis, 2511 03:04:40,625 --> 03:04:43,125 with, eventually, name and a house passed in. 2512 03:04:43,125 --> 03:04:46,833 And then we were using the double underscore init method 2513 03:04:46,833 --> 03:04:52,708 to initialize the self.name and the self.house instance variables 2514 03:04:52,708 --> 03:04:55,333 therein to those respective values. 2515 03:04:55,333 --> 03:04:57,750 In this latest version of the sorting hat, 2516 03:04:57,750 --> 03:05:02,166 I haven't bothered with self anywhere, only because, conceptually, I 2517 03:05:02,166 --> 03:05:05,708 don't need or want there to be multiple hats in the world. 2518 03:05:05,708 --> 03:05:11,041 I'm just using the class as a container to bundle up this list of houses, 2519 03:05:11,041 --> 03:05:12,500 this sorting functionality. 2520 03:05:12,500 --> 03:05:14,875 Maybe eventually all add more functionality to it. 2521 03:05:14,875 --> 03:05:15,875 But that's it. 2522 03:05:15,875 --> 03:05:18,833 And so sometimes you can use object-oriented programming 2523 03:05:18,833 --> 03:05:22,458 in this somewhat different way when you want there to be functionality 2524 03:05:22,458 --> 03:05:25,333 but it's not specific to any one specific hat. 2525 03:05:25,333 --> 03:05:29,416 It's specific to the sorting hat itself. 2526 03:05:29,416 --> 03:05:33,958 How about one other question now, on these class variables or methods-- just 2527 03:05:33,958 --> 03:05:37,000 another way of using object-oriented programming but to solve 2528 03:05:37,000 --> 03:05:38,541 a somewhat different problem? 2529 03:05:38,541 --> 03:05:40,916 AUDIENCE: Well, what's the difference between the class 2530 03:05:40,916 --> 03:05:44,833 hat and a function of hat? 2531 03:05:44,833 --> 03:05:46,166 DAVID J. MALAN: A good question. 2532 03:05:46,166 --> 03:05:48,500 So why are we using a class at all and not just 2533 03:05:48,500 --> 03:05:51,916 having a file called hat.py with a variable called 2534 03:05:51,916 --> 03:05:54,500 houses and a function called sort? 2535 03:05:54,500 --> 03:05:56,166 Why are we adding this complexity? 2536 03:05:56,166 --> 03:05:58,958 In this particular case, we don't necessarily need to. 2537 03:05:58,958 --> 03:06:01,166 I could absolutely go in here. 2538 03:06:01,166 --> 03:06:02,833 I could get rid of the class. 2539 03:06:02,833 --> 03:06:05,625 I could undo this indentation. 2540 03:06:05,625 --> 03:06:07,666 I could get rid of this decorator. 2541 03:06:07,666 --> 03:06:09,541 And I could get rid of hat dot. 2542 03:06:09,541 --> 03:06:12,291 And I could just do this and additionally 2543 03:06:12,291 --> 03:06:14,208 let's say, let's get rid of class here. 2544 03:06:14,208 --> 03:06:15,833 Let's get rid of class here. 2545 03:06:15,833 --> 03:06:19,833 And now run Python of hat.py, Enter, and it still works. 2546 03:06:19,833 --> 03:06:22,125 Put Harry in the wrong house, but that's what we have-- 2547 03:06:22,125 --> 03:06:23,208 what happens randomly. 2548 03:06:23,208 --> 03:06:24,500 That's fine, too. 2549 03:06:24,500 --> 03:06:27,750 What we're introducing today, by way of object-oriented programming, 2550 03:06:27,750 --> 03:06:30,208 is just a different way of modeling the world. 2551 03:06:30,208 --> 03:06:32,916 It's not really compelling with an example like this, 2552 03:06:32,916 --> 03:06:35,125 frankly, that's relatively simple. 2553 03:06:35,125 --> 03:06:36,125 It's not very complex. 2554 03:06:36,125 --> 03:06:37,416 There's not much functionality. 2555 03:06:37,416 --> 03:06:40,875 Honestly, the version that we just typed up-- these 10 lines-- this is fine. 2556 03:06:40,875 --> 03:06:42,208 This solves this problem. 2557 03:06:42,208 --> 03:06:45,708 But as our code gets longer, as we start collaborating with other people, 2558 03:06:45,708 --> 03:06:48,666 as the problems we're trying to solve with code get more sophisticated, 2559 03:06:48,666 --> 03:06:52,125 you're going to find that your code gets messy quickly. 2560 03:06:52,125 --> 03:06:55,458 And you're going to find that you have a huge number of functions, for instance, 2561 03:06:55,458 --> 03:06:56,500 in one file. 2562 03:06:56,500 --> 03:06:59,958 And some of them are related to each other, but some of them are not. 2563 03:06:59,958 --> 03:07:02,625 Well, at that point, wouldn't it be nice to just organize them 2564 03:07:02,625 --> 03:07:03,500 a little differently? 2565 03:07:03,500 --> 03:07:06,500 And in the world of Harry Potter, let's have a class for student; 2566 03:07:06,500 --> 03:07:09,916 let's have a class for Professor; let's have a class for the sorting hat; 2567 03:07:09,916 --> 03:07:11,708 let's have a class for something else. 2568 03:07:11,708 --> 03:07:14,625 And so once your world gets much more complicated than some 2569 03:07:14,625 --> 03:07:17,791 of the demonstrations we do here in class when we want to focus 2570 03:07:17,791 --> 03:07:21,500 on individual ideas, object-oriented programming is just a way 2571 03:07:21,500 --> 03:07:24,083 of encapsulating related data-- 2572 03:07:24,083 --> 03:07:27,291 that is, variables-- related functionality-- that is, methods-- 2573 03:07:27,291 --> 03:07:29,583 inside of things that have names. 2574 03:07:29,583 --> 03:07:31,625 These things are called classes. 2575 03:07:31,625 --> 03:07:33,750 So it's just another way to solve problems. 2576 03:07:33,750 --> 03:07:36,125 And when we focused on libraries a couple of weeks back, 2577 03:07:36,125 --> 03:07:38,791 that, too, was another solution to the same problem. 2578 03:07:38,791 --> 03:07:42,291 You could define your own modules or packages, put some of your data 2579 03:07:42,291 --> 03:07:44,708 and/or functionality in there, and that's fine, too. 2580 03:07:44,708 --> 03:07:48,000 And sometimes which one you should use overlaps. 2581 03:07:48,000 --> 03:07:51,333 If you're familiar with Venn diagrams, the overlapping region 2582 03:07:51,333 --> 03:07:54,791 might mean that you could use a class; you could use a module or a package; 2583 03:07:54,791 --> 03:07:57,500 you could just use a single local file. 2584 03:07:57,500 --> 03:07:59,916 Over time, you'll develop an instinct and maybe even 2585 03:07:59,916 --> 03:08:04,291 a personal preference for which tool to use. 2586 03:08:04,291 --> 03:08:08,416 All right, let me propose, now, that we apply this same idea of a class method 2587 03:08:08,416 --> 03:08:10,583 to clean up one other thing as well. 2588 03:08:10,583 --> 03:08:16,041 Let me close that hat.py and reopen student.py as we left it earlier, 2589 03:08:16,041 --> 03:08:19,500 and let me go ahead and simplify it just a little bit. 2590 03:08:19,500 --> 03:08:22,750 I'm going to go ahead and get rid of the properties, 2591 03:08:22,750 --> 03:08:24,666 not because there's anything, wrong with them, 2592 03:08:24,666 --> 03:08:27,416 but just because I want us to focus on some of the key ideas 2593 03:08:27,416 --> 03:08:29,333 when we began with this program. 2594 03:08:29,333 --> 03:08:31,750 So I'm going to go ahead and keep main as well. 2595 03:08:31,750 --> 03:08:35,583 I'm not going to adversarially try to change Henry's address there. 2596 03:08:35,583 --> 03:08:38,666 I'm going to instead go ahead, though, and just print the student. 2597 03:08:38,666 --> 03:08:41,750 But this is the thing I want to focus on here. 2598 03:08:41,750 --> 03:08:46,541 This, in our previous student examples, was a missed opportunity 2599 03:08:46,541 --> 03:08:48,125 to clean up my code. 2600 03:08:48,125 --> 03:08:49,708 Well, what do I mean by that? 2601 03:08:49,708 --> 03:08:51,750 Well, up here at the top of this file-- even 2602 03:08:51,750 --> 03:08:53,958 though I've simplified it, but getting rid of the properties 2603 03:08:53,958 --> 03:08:55,708 and all of that error checking-- because I 2604 03:08:55,708 --> 03:08:58,708 want to focus on the essence of this class now-- just the student's name 2605 03:08:58,708 --> 03:09:00,875 and the house and the printing thereof. 2606 03:09:00,875 --> 03:09:05,000 This is, by nature of classes in object-oriented programming, 2607 03:09:05,000 --> 03:09:09,666 theoretically, all of my student-specific functionality. 2608 03:09:09,666 --> 03:09:14,083 That is to say, if I have functionality and data related to a student, you, 2609 03:09:14,083 --> 03:09:16,583 the programmer, my colleague, would assume 2610 03:09:16,583 --> 03:09:18,875 that it's all bundled up, encapsulated, so 2611 03:09:18,875 --> 03:09:20,791 to speak, inside of the student class. 2612 03:09:20,791 --> 03:09:24,208 And yet, if you scroll down further, what is this? 2613 03:09:24,208 --> 03:09:27,458 There's a function called get_student that just exists elsewhere 2614 03:09:27,458 --> 03:09:31,166 in this file that prompts the user for a name, prompts the user for a house, 2615 03:09:31,166 --> 03:09:34,083 creates the student object, and then returns. 2616 03:09:34,083 --> 03:09:35,500 That's not wrong. 2617 03:09:35,500 --> 03:09:36,208 It works. 2618 03:09:36,208 --> 03:09:39,041 And we saw many, many times it kept working. 2619 03:09:39,041 --> 03:09:42,208 But this is a little weird because, if this 2620 03:09:42,208 --> 03:09:45,208 is a function that helps you get a student, 2621 03:09:45,208 --> 03:09:48,916 helps you get the name of a student and the house of a student, why isn't 2622 03:09:48,916 --> 03:09:51,125 that functionality in the class itself? 2623 03:09:51,125 --> 03:09:54,291 After all, as my code gets more and more complicated and does more things, 2624 03:09:54,291 --> 03:09:56,125 I'm going to be looking at the student class 2625 03:09:56,125 --> 03:09:57,750 for all student-related functionality. 2626 03:09:57,750 --> 03:10:00,625 I'm not going to be scrolling down, expecting that, oh, maybe there's 2627 03:10:00,625 --> 03:10:04,708 some other student functionality just randomly later in this file. 2628 03:10:04,708 --> 03:10:05,958 So it's not wrong. 2629 03:10:05,958 --> 03:10:09,625 But this is, again, evidence of maybe bad design-- 2630 03:10:09,625 --> 03:10:11,541 not so much with this small program. 2631 03:10:11,541 --> 03:10:14,458 But this is an example, again, of code smell. 2632 03:10:14,458 --> 03:10:16,000 Something smells a little off here. 2633 03:10:16,000 --> 03:10:17,791 This is probably going to get us in trouble 2634 03:10:17,791 --> 03:10:20,791 by separating related functionality. 2635 03:10:20,791 --> 03:10:24,541 So again it's a design principle, not a correctness concern. 2636 03:10:24,541 --> 03:10:27,958 But class methods allow us to address this, too. 2637 03:10:27,958 --> 03:10:29,791 Let me go ahead and do this. 2638 03:10:29,791 --> 03:10:32,666 I'm going to delete get_student all together, leaving 2639 03:10:32,666 --> 03:10:35,458 only main as my other function here. 2640 03:10:35,458 --> 03:10:38,541 And inside of my student class, I'm going to do this. 2641 03:10:38,541 --> 03:10:42,375 I'm going to define a function, even more simply called, get. 2642 03:10:42,375 --> 03:10:45,291 And by nature of how class methods work, it's 2643 03:10:45,291 --> 03:10:47,625 going to take in the name of the class itself 2644 03:10:47,625 --> 03:10:49,833 or a reference thereto as an argument. 2645 03:10:49,833 --> 03:10:53,083 And I'm going to move the functionality from get_student into the student 2646 03:10:53,083 --> 03:10:53,583 class. 2647 03:10:53,583 --> 03:10:58,750 And I'm going to do this-- name equals input, quote, unquote, name, house 2648 03:10:58,750 --> 03:11:01,708 equals input, quote, unquote, house. 2649 03:11:01,708 --> 03:11:04,291 And then what this function is going to do 2650 03:11:04,291 --> 03:11:11,333 is return a new student object by calling class, which, again, is just 2651 03:11:11,333 --> 03:11:14,250 an automatically passed-in reference to the class 2652 03:11:14,250 --> 03:11:17,666 itself, passing in name and house. 2653 03:11:17,666 --> 03:11:22,916 And I will admit this syntax seems a little strange that now I'm calling cls 2654 03:11:22,916 --> 03:11:24,375 and I'm passing in these arguments. 2655 03:11:24,375 --> 03:11:26,125 But let me do one final fix here. 2656 03:11:26,125 --> 03:11:28,041 Let me go to the top of this function and more 2657 03:11:28,041 --> 03:11:31,125 explicitly say this is a class method. 2658 03:11:31,125 --> 03:11:34,541 This solves a potential chicken and the egg problem, so to speak, 2659 03:11:34,541 --> 03:11:37,291 whereby one needs to come before the other, potentially. 2660 03:11:37,291 --> 03:11:39,250 So what am I doing here? 2661 03:11:39,250 --> 03:11:43,916 Inside of my student class, I now have a function called get. 2662 03:11:43,916 --> 03:11:47,333 It is, I shall claim, a class method what does that mean. 2663 03:11:47,333 --> 03:11:53,000 It just means I can call this method without instantiating a student 2664 03:11:53,000 --> 03:11:54,125 object first. 2665 03:11:54,125 --> 03:11:57,333 Therein lies the potential chicken and the egg problem. 2666 03:11:57,333 --> 03:12:00,375 And if unfamiliar, that's an expression, meaning, well, and did the world 2667 03:12:00,375 --> 03:12:03,291 have chickens first that laid eggs, or was there an egg 2668 03:12:03,291 --> 03:12:06,958 that then yielded the chickens, but how did the egg get there? 2669 03:12:06,958 --> 03:12:08,791 It's this weird, circular problem. 2670 03:12:08,791 --> 03:12:10,208 And that's what we're facing here. 2671 03:12:10,208 --> 03:12:16,375 It would be weird if you had to create a student object in order to call get, 2672 03:12:16,375 --> 03:12:19,791 in order to get another student object. 2673 03:12:19,791 --> 03:12:20,875 That sounds messy. 2674 03:12:20,875 --> 03:12:24,833 Let's just get a student via a class method 2675 03:12:24,833 --> 03:12:29,125 that, by definition, does not require you to create a student object first. 2676 03:12:29,125 --> 03:12:33,166 Just like the hat, in its final form, we use the hat class 2677 03:12:33,166 --> 03:12:35,833 to just say Hat, capital H, dot sort. 2678 03:12:35,833 --> 03:12:37,458 We didn't need to create a hat first. 2679 03:12:37,458 --> 03:12:39,708 We just used the class itself. 2680 03:12:39,708 --> 03:12:41,791 So what am I going to do here now? 2681 03:12:41,791 --> 03:12:43,041 Let me go down to main. 2682 03:12:43,041 --> 03:12:46,791 And instead of saying get_student, notice what I can now do. 2683 03:12:46,791 --> 03:12:51,625 Student.get, and everything else can stay the same. 2684 03:12:51,625 --> 03:12:55,416 All I've done now is I've migrated all of my logic 2685 03:12:55,416 --> 03:12:58,541 from get_student, which was this own standalone function, 2686 03:12:58,541 --> 03:13:01,291 but clearly related to students by name. 2687 03:13:01,291 --> 03:13:05,458 I've moved the same code, really, to inside 2688 03:13:05,458 --> 03:13:09,458 of the student class in a more simply named function called get. 2689 03:13:09,458 --> 03:13:11,541 But I could still call it get_student if I want. 2690 03:13:11,541 --> 03:13:14,791 It just seems a little redundant to call it get_student in a student class, 2691 03:13:14,791 --> 03:13:16,291 so I'm simplifying. 2692 03:13:16,291 --> 03:13:20,208 So I have a method called get, but I'm calling it a class method 2693 03:13:20,208 --> 03:13:22,333 to avoid that chicken and the egg problem. 2694 03:13:22,333 --> 03:13:27,958 I want to be able to call a get without having a student object in my universe 2695 03:13:27,958 --> 03:13:28,791 already. 2696 03:13:28,791 --> 03:13:32,041 And the syntax for that is @classmethod. 2697 03:13:32,041 --> 03:13:35,333 The convention is to give this method at least one argument, 2698 03:13:35,333 --> 03:13:38,291 by convention called cls for class, which is just going 2699 03:13:38,291 --> 03:13:40,125 to be a reference to the class itself. 2700 03:13:40,125 --> 03:13:43,500 Lines 11 and 12 are identical to what they've always been. 2701 03:13:43,500 --> 03:13:46,666 And get_student-- the only new syntax here is this, 2702 03:13:46,666 --> 03:13:50,500 but this, again, is one of the features of object-oriented programming. 2703 03:13:50,500 --> 03:13:57,791 You can now instantiate a student object by just using cls that's passed in. 2704 03:13:57,791 --> 03:14:00,583 I technically could use Student, capital S, 2705 03:14:00,583 --> 03:14:03,333 but it turns out I'm doing what's more conventional because this 2706 03:14:03,333 --> 03:14:07,458 will both solve and avoid problems down the line with more complicated code. 2707 03:14:07,458 --> 03:14:11,916 This line here, on line 13, just means create an object of the current class. 2708 03:14:11,916 --> 03:14:12,708 What class is that? 2709 03:14:12,708 --> 03:14:13,791 Well, whatever cls is. 2710 03:14:13,791 --> 03:14:17,458 Well, that, by definition of how it all works, is going to be student. 2711 03:14:17,458 --> 03:14:22,250 And I want you to initialize it, as always, with name and house. 2712 03:14:22,250 --> 03:14:25,666 So now, scrolling down, my code is this. 2713 03:14:25,666 --> 03:14:27,875 And this is just nice to read. 2714 03:14:27,875 --> 03:14:31,791 You perhaps have to acquire a taste for this-- and I sound a little odd saying, 2715 03:14:31,791 --> 03:14:32,750 this is nice to read. 2716 03:14:32,750 --> 03:14:36,125 But indeed, student.get just tells me what's going on. 2717 03:14:36,125 --> 03:14:37,375 I'm going to get a student. 2718 03:14:37,375 --> 03:14:40,958 I don't need a separate function written by me called get_student in the file 2719 03:14:40,958 --> 03:14:41,750 itself. 2720 03:14:41,750 --> 03:14:44,875 The get functionality is built into the class. 2721 03:14:44,875 --> 03:14:47,416 All my student-related code now is together. 2722 03:14:47,416 --> 03:14:51,041 So let me go down to my terminal window and run Python of student.py, Enter. 2723 03:14:51,041 --> 03:14:52,083 Let's type in Harry. 2724 03:14:52,083 --> 03:14:53,416 Let's type in Gryffindor. 2725 03:14:53,416 --> 03:14:54,958 And we're back to where we began. 2726 03:14:54,958 --> 03:15:00,541 But, but, but everything related to students, now, is in this here class. 2727 03:15:00,541 --> 03:15:04,500 The only other thing in the file is main and this conditional 2728 03:15:04,500 --> 03:15:08,458 that we always use to avoid accidentally executing main when we're making 2729 03:15:08,458 --> 03:15:10,833 a module or a package or the like. 2730 03:15:10,833 --> 03:15:13,541 So again, a solution to a problem-- 2731 03:15:13,541 --> 03:15:16,875 not a big one in the case of a relatively small program, but one 2732 03:15:16,875 --> 03:15:19,041 that you will eventually encounter as your programs 2733 03:15:19,041 --> 03:15:24,083 get longer and longer, with more and more entities to represent. 2734 03:15:24,083 --> 03:15:29,000 Questions now on this use of a class method. 2735 03:15:29,000 --> 03:15:32,291 MICHAEL: Does the class have to be defined before the main function, 2736 03:15:32,291 --> 03:15:34,458 in terms of the order of the program? 2737 03:15:34,458 --> 03:15:36,083 DAVID J. MALAN: A really good question. 2738 03:15:36,083 --> 03:15:37,291 So when in doubt, let's try this. 2739 03:15:37,291 --> 03:15:38,666 So let's try to change the order. 2740 03:15:38,666 --> 03:15:41,208 Let's move main to the top, which I've often encouraged. 2741 03:15:41,208 --> 03:15:44,208 So let's go ahead and, above the class, do this. 2742 03:15:44,208 --> 03:15:47,000 And notice now that, technically, line two 2743 03:15:47,000 --> 03:15:51,458 is mentioning student, which does not exist until line 6 and below. 2744 03:15:51,458 --> 03:15:54,666 Let me go ahead and clear my terminal and run Python of student.py. 2745 03:15:54,666 --> 03:15:55,875 So far, so good. 2746 03:15:55,875 --> 03:15:58,750 Harry-- Gryffindor, OK. 2747 03:15:58,750 --> 03:16:00,458 Indeed, Harry's from Gryffindor. 2748 03:16:00,458 --> 03:16:03,541 The reason, Michael, it does not matter in this case 2749 03:16:03,541 --> 03:16:06,791 is because we're not actually calling main until the very end. 2750 03:16:06,791 --> 03:16:10,375 And just as in the past, that means that Python has a chance to read everything, 2751 03:16:10,375 --> 03:16:11,625 top to bottom, left to right. 2752 03:16:11,625 --> 03:16:13,083 So everything exists. 2753 03:16:13,083 --> 03:16:17,458 I would say, generally classes are defined at the top of the file. 2754 03:16:17,458 --> 03:16:21,916 However, it would be even maybe cleaner to move the Classes definition 2755 03:16:21,916 --> 03:16:25,083 to its own file and then import it, so essentially 2756 03:16:25,083 --> 03:16:28,750 to make reusable code by putting it into your own module or package 2757 03:16:28,750 --> 03:16:31,083 so that not just this program but many others 2758 03:16:31,083 --> 03:16:33,541 can use that definition of student as well. 2759 03:16:33,541 --> 03:16:37,750 Other questions now on classes, class methods, or the like. 2760 03:16:37,750 --> 03:16:44,833 AUDIENCE: I wanted to ask, is there a way to declare all the possible-- 2761 03:16:44,833 --> 03:16:47,583 all the possible attributes of the class? 2762 03:16:47,583 --> 03:16:51,000 Because it looks so inconsistent. 2763 03:16:51,000 --> 03:16:52,750 DAVID J. MALAN: Well, so my takeaway there 2764 03:16:52,750 --> 03:16:55,208 is this is Python's approach to these principles. 2765 03:16:55,208 --> 03:16:57,791 Different languages, like Java, just take a different approach 2766 03:16:57,791 --> 03:16:59,666 but have very similar features. 2767 03:16:59,666 --> 03:17:01,500 The syntax just tends to vary. 2768 03:17:01,500 --> 03:17:05,208 And this is how the Python community chose to implement this idea. 2769 03:17:05,208 --> 03:17:08,750 The right mental model, ultimately, is that these instance variables, 2770 03:17:08,750 --> 03:17:14,958 instant methods belong to or operate on specific objects-- 2771 03:17:14,958 --> 03:17:17,750 a specific student, a specific hat. 2772 03:17:17,750 --> 03:17:22,500 Class variables and class methods operate on the entire class 2773 03:17:22,500 --> 03:17:26,125 itself or, in turn, all objects of that class, which 2774 03:17:26,125 --> 03:17:30,000 we've not seen a demonstration of, but it's a higher level concept. 2775 03:17:30,000 --> 03:17:32,583 So it turns out, besides these class methods, 2776 03:17:32,583 --> 03:17:35,625 which are distinct from those instance methods, which, to be fair, 2777 03:17:35,625 --> 03:17:39,375 do not have their own decorator-- they just are, by default, instance method, 2778 03:17:39,375 --> 03:17:42,458 there's yet other types of Methods You can have in classes in Python. 2779 03:17:42,458 --> 03:17:44,875 They tend to be called static methods, and they, too, 2780 03:17:44,875 --> 03:17:48,041 come with another decorator called @static method, which 2781 03:17:48,041 --> 03:17:49,666 is a rabbit hole we won't go down. 2782 03:17:49,666 --> 03:17:52,125 But realize that there is yet other functionality 2783 03:17:52,125 --> 03:17:54,750 that you can leverage within object-oriented programming. 2784 03:17:54,750 --> 03:17:59,041 But what we thought we'd do is focus really on some final core features 2785 03:17:59,041 --> 03:18:02,166 that you see not just in Python but other languages as well. 2786 03:18:02,166 --> 03:18:05,750 And perhaps one of the most compelling features of object-oriented programming 2787 03:18:05,750 --> 03:18:09,375 that we haven't yet used explicitly-- though it turns out we've seen 2788 03:18:09,375 --> 03:18:11,458 implicitly over the past weeks-- 2789 03:18:11,458 --> 03:18:13,625 is this notion of inheritance. 2790 03:18:13,625 --> 03:18:16,250 It turns out, via object-oriented programming, 2791 03:18:16,250 --> 03:18:19,666 there's actually an opportunity to design your classes 2792 03:18:19,666 --> 03:18:25,125 in a hierarchical fashion, whereby you can have one class inherit from 2793 03:18:25,125 --> 03:18:30,833 or borrow attributes-- that is, methods or variables from another class 2794 03:18:30,833 --> 03:18:33,291 if they all have those in common. 2795 03:18:33,291 --> 03:18:35,083 So what do I mean by this here? 2796 03:18:35,083 --> 03:18:39,708 Well, let me propose that we implement, over in VS Code here, 2797 03:18:39,708 --> 03:18:42,250 a brand new file called wizard.py. 2798 03:18:42,250 --> 03:18:45,583 Let me go ahead and run code of wizard.py. 2799 03:18:45,583 --> 03:18:50,333 And then let's start as before, defining a class called student. 2800 03:18:50,333 --> 03:18:54,416 And let's go ahead and first define the underscore underscore, init method, 2801 03:18:54,416 --> 03:18:57,291 which of course, is minimally going to take an argument traditionally 2802 03:18:57,291 --> 03:18:58,000 called self. 2803 03:18:58,000 --> 03:19:01,375 And in this case, let's also have it take as before a name and a house. 2804 03:19:01,375 --> 03:19:03,875 And then in this init method, let's go ahead 2805 03:19:03,875 --> 03:19:08,208 and assign the instance variables-- self.name = name, 2806 03:19:08,208 --> 03:19:11,208 and self.house = house. 2807 03:19:11,208 --> 03:19:13,375 Let's assume that there's some other functionality 2808 03:19:13,375 --> 03:19:15,083 in this class as well-- dot, dot, dot. 2809 03:19:15,083 --> 03:19:18,416 But let's move on now to implementing the notion of a professor 2810 03:19:18,416 --> 03:19:20,208 in the wizarding world as well. 2811 03:19:20,208 --> 03:19:24,625 So for this class, let's call it Professor. 2812 03:19:24,625 --> 03:19:26,958 And a professor, let's say, is also going 2813 03:19:26,958 --> 03:19:29,416 to have its own initialization method. 2814 03:19:29,416 --> 03:19:30,708 So __ init. 2815 03:19:30,708 --> 03:19:32,000 It's going to take self-- 2816 03:19:32,000 --> 03:19:33,791 always as the first argument. 2817 03:19:33,791 --> 03:19:35,500 A professor also has a name. 2818 03:19:35,500 --> 03:19:37,166 So we'll pass that in second, too. 2819 03:19:37,166 --> 03:19:39,958 And even though some professors are heads of houses, 2820 03:19:39,958 --> 03:19:42,041 let's assume that a professor is really identified 2821 03:19:42,041 --> 03:19:45,500 by their name and their subject area-- the class that they teach. 2822 03:19:45,500 --> 03:19:48,041 So we'll call this third argument, subject. 2823 03:19:48,041 --> 03:19:51,791 Now, as before, let's go ahead and assign self.name = name, 2824 03:19:51,791 --> 03:19:56,083 and let's assign self.subject = subject here. 2825 03:19:56,083 --> 03:19:59,541 And as before, let's assume that there's some more functionality associated 2826 03:19:59,541 --> 03:20:01,083 with professors as well. 2827 03:20:01,083 --> 03:20:05,625 Well, what do you notice already here in my definitions of students 2828 03:20:05,625 --> 03:20:07,333 and professors? 2829 03:20:07,333 --> 03:20:11,750 Typically, we're a bit reluctant to allow for any redundancy in our code. 2830 03:20:11,750 --> 03:20:16,041 And here, I feel like my init method is taking a name for students; 2831 03:20:16,041 --> 03:20:18,625 my init method is also taking a name for a professor; 2832 03:20:18,625 --> 03:20:22,458 and I have these identical lines of code, like self.name = name. 2833 03:20:22,458 --> 03:20:25,208 And this is only going to get exacerbated if I now go and add 2834 03:20:25,208 --> 03:20:26,083 some error checking. 2835 03:20:26,083 --> 03:20:29,916 So for instance, how about if not name, we 2836 03:20:29,916 --> 03:20:32,083 should probably be in the habit of raising something 2837 03:20:32,083 --> 03:20:36,208 like a value error in an explanatory message, like "Missing name." 2838 03:20:36,208 --> 03:20:37,166 And you know what? 2839 03:20:37,166 --> 03:20:39,750 If a professor is missing their name, I should probably 2840 03:20:39,750 --> 03:20:41,291 copy, paste that code down here. 2841 03:20:41,291 --> 03:20:43,666 And that's where red flags should be going off, 2842 03:20:43,666 --> 03:20:45,916 whereby, as soon as you start copy pasting code, 2843 03:20:45,916 --> 03:20:50,208 there's probably a better way so that we can write the code once and perhaps 2844 03:20:50,208 --> 03:20:51,500 reuse it in some way. 2845 03:20:51,500 --> 03:20:54,916 And here, too, object-oriented programming offers a solution. 2846 03:20:54,916 --> 03:20:58,041 It turns out that object-oriented programming in Python 2847 03:20:58,041 --> 03:21:03,458 also supports inheritance, whereby you can define multiple classes that 2848 03:21:03,458 --> 03:21:05,125 somehow relate to one another. 2849 03:21:05,125 --> 03:21:07,833 They don't need to exist in parallel in this way. 2850 03:21:07,833 --> 03:21:10,375 There could actually be some hierarchy between them. 2851 03:21:10,375 --> 03:21:12,333 So for instance, in the wizarding world, we 2852 03:21:12,333 --> 03:21:15,583 could argue that both a student and a professor are, at the end of the day, 2853 03:21:15,583 --> 03:21:16,291 Wizards. 2854 03:21:16,291 --> 03:21:19,666 So maybe what we should really define is a third class, 2855 03:21:19,666 --> 03:21:21,958 for instance, called wizard, that has any 2856 03:21:21,958 --> 03:21:25,916 of the common attributes for students and professors alike. 2857 03:21:25,916 --> 03:21:27,791 And for now, we've kept it relatively simple. 2858 03:21:27,791 --> 03:21:30,666 The only thing they have in common is a name and a name, 2859 03:21:30,666 --> 03:21:32,458 in student and professor, respectively. 2860 03:21:32,458 --> 03:21:35,416 So why don't we minimally factor that out first? 2861 03:21:35,416 --> 03:21:37,083 All right, so let me go ahead here. 2862 03:21:37,083 --> 03:21:39,708 And just to keep things organized, at the top of my file, let's 2863 03:21:39,708 --> 03:21:41,833 define a third class called Wizard. 2864 03:21:41,833 --> 03:21:45,208 And a wizard will have its own initialization method. 2865 03:21:45,208 --> 03:21:49,541 So def __init__(self), as always. 2866 03:21:49,541 --> 03:21:52,333 And a wizard, let's say for now, is only going 2867 03:21:52,333 --> 03:21:55,625 to be initialized with their name in this way. 2868 03:21:55,625 --> 03:21:58,791 And now, I'm going to go ahead and do some of that error checking. 2869 03:21:58,791 --> 03:22:02,708 So if not name will raise a value error in the wizard class. 2870 03:22:02,708 --> 03:22:06,041 Otherwise, we'll go ahead and do self name equals name, 2871 03:22:06,041 --> 03:22:09,666 and, heck, dot, dot, dot, maybe some other functionality as well. 2872 03:22:09,666 --> 03:22:13,041 But not a subject, which is specific to professors, and not a house, 2873 03:22:13,041 --> 03:22:15,166 which I've claimed is specific to students. 2874 03:22:15,166 --> 03:22:18,375 Now, I think we can begin to maybe remove 2875 03:22:18,375 --> 03:22:20,833 some of the redundancies in our other classes here. 2876 03:22:20,833 --> 03:22:24,416 So for instance, down with student, why don't I 2877 03:22:24,416 --> 03:22:28,750 go ahead and remove this error checking here and remove this error-- 2878 03:22:28,750 --> 03:22:32,166 this assignment of self.name = name because I'm already 2879 03:22:32,166 --> 03:22:33,291 doing that in Wizard. 2880 03:22:33,291 --> 03:22:35,958 And similarly, down here, in Professor, why don't I do the same? 2881 03:22:35,958 --> 03:22:37,583 Let's get rid of the error checking. 2882 03:22:37,583 --> 03:22:41,083 Let's get rid of self.name = name because, again, I'm doing that already 2883 03:22:41,083 --> 03:22:43,041 up there for Wizard as well. 2884 03:22:43,041 --> 03:22:45,916 But at the moment, even though they're all in the same file, 2885 03:22:45,916 --> 03:22:50,666 I haven't told Python that a student is a wizard and a professor is a wizard. 2886 03:22:50,666 --> 03:22:53,000 So I really need to link these two together. 2887 03:22:53,000 --> 03:22:57,500 And the way you can prescribe inheritance, whereby one class should 2888 03:22:57,500 --> 03:23:00,041 inherit from another, or conversely, one class 2889 03:23:00,041 --> 03:23:03,541 should descend from another-- we can do this. 2890 03:23:03,541 --> 03:23:05,125 I can say class Student. 2891 03:23:05,125 --> 03:23:08,416 But before the colon, I can go in and say in parentheses, 2892 03:23:08,416 --> 03:23:13,916 a student inherits from, or is a subclass of wizard, which, conversely, 2893 03:23:13,916 --> 03:23:16,666 is the superclass of the student class. 2894 03:23:16,666 --> 03:23:19,583 So this just means that, when I define a student class, 2895 03:23:19,583 --> 03:23:23,958 go ahead and inherit all of the characteristics of a wizard as well. 2896 03:23:23,958 --> 03:23:26,875 And I'm going to do the same thing for Professor. 2897 03:23:26,875 --> 03:23:30,166 So (Wizard) after the class name Professor, 2898 03:23:30,166 --> 03:23:33,833 and that's going to give me access to some of that same functionality. 2899 03:23:33,833 --> 03:23:37,458 But because my student class and my professor class 2900 03:23:37,458 --> 03:23:39,416 still have their same init methods, those 2901 03:23:39,416 --> 03:23:41,291 are the methods that are going to get called. 2902 03:23:41,291 --> 03:23:44,916 Whenever I create a student in code or I create a professor in code, 2903 03:23:44,916 --> 03:23:47,625 I need to somehow explicitly say that I also 2904 03:23:47,625 --> 03:23:51,916 want to use the functionality in the Wizard class's init method. 2905 03:23:51,916 --> 03:23:54,916 And the way to do this in Python is as follows. 2906 03:23:54,916 --> 03:23:56,958 Let me go into my init method for Student, 2907 03:23:56,958 --> 03:23:59,958 and let me call super, with no arguments, which 2908 03:23:59,958 --> 03:24:03,333 is a reference to the superclass of this class. 2909 03:24:03,333 --> 03:24:05,916 So if this class is Student, the superclass-- that is, 2910 03:24:05,916 --> 03:24:07,791 the parent class-- is Wizard. 2911 03:24:07,791 --> 03:24:13,875 So super() will have the effect of accessing the superclass. 2912 03:24:13,875 --> 03:24:17,750 And then I'm going to go ahead and explicitly call its init method, 2913 03:24:17,750 --> 03:24:21,416 and I'm going to pass to the Wizard's init method 2914 03:24:21,416 --> 03:24:24,958 the name that the student's init method was passed. 2915 03:24:24,958 --> 03:24:28,000 And I'm going to go ahead and do the same down here in Wizard. 2916 03:24:28,000 --> 03:24:29,666 This is one line of copy, paste. 2917 03:24:29,666 --> 03:24:32,375 But I think I'm OK with it here because it's still 2918 03:24:32,375 --> 03:24:34,291 allowing me to do all of the name assignment 2919 03:24:34,291 --> 03:24:37,000 and the error checking up in the Wizard class instead. 2920 03:24:37,000 --> 03:24:42,250 I think we're OK now by just calling super.init for both student 2921 03:24:42,250 --> 03:24:43,416 and Professor alike. 2922 03:24:43,416 --> 03:24:47,208 Now, admittedly, this syntax is definitely out there-- the fact 2923 03:24:47,208 --> 03:24:49,541 that we're calling super in parentheses and dots 2924 03:24:49,541 --> 03:24:52,458 and underscore underscore on the left and the right of init here, 2925 03:24:52,458 --> 03:24:54,875 but it's just a combination of these two ideas. 2926 03:24:54,875 --> 03:25:00,916 super() is a way of programmatically accessing a current class's parent 2927 03:25:00,916 --> 03:25:05,291 class, or superclass, and __init, of course, is just referring to, now, 2928 03:25:05,291 --> 03:25:08,041 that class's own initialization method. 2929 03:25:08,041 --> 03:25:09,958 Now, per the dot, dot, dot-- so there could be 2930 03:25:09,958 --> 03:25:11,500 a lot more going on in these classes. 2931 03:25:11,500 --> 03:25:15,208 But what's nice now is that Wizard as a class 2932 03:25:15,208 --> 03:25:18,291 is taking care of all of the assignment of a wizard's name, 2933 03:25:18,291 --> 03:25:20,458 whether that wizard is a student or a professor. 2934 03:25:20,458 --> 03:25:22,416 And it's even doing some error checking to make 2935 03:25:22,416 --> 03:25:25,125 sure the name was actually passed in. 2936 03:25:25,125 --> 03:25:29,166 Meanwhile, student is inheriting all of that functionality 2937 03:25:29,166 --> 03:25:32,333 and using it by calling the superclass's own init method. 2938 03:25:32,333 --> 03:25:35,041 But it's additionally taking the house, that's presumably 2939 03:25:35,041 --> 03:25:37,375 passed into the student constructor function, 2940 03:25:37,375 --> 03:25:40,833 and assigning it to its own instance variable-- self.house, 2941 03:25:40,833 --> 03:25:44,458 and similarly, professor, or restoring in self.subject 2942 03:25:44,458 --> 03:25:47,875 the subject that was passed into that one as well. 2943 03:25:47,875 --> 03:25:49,916 Now, how might we use these classes? 2944 03:25:49,916 --> 03:25:53,500 Well, we'll continue to wave our hands with a little bit of detail here. 2945 03:25:53,500 --> 03:25:56,750 But at the bottom of this file, or any other file that imports this one, 2946 03:25:56,750 --> 03:25:58,833 I could now write code like this. 2947 03:25:58,833 --> 03:26:01,208 I could create a student variable and assign 2948 03:26:01,208 --> 03:26:03,458 it the return value of the student constructor call. 2949 03:26:03,458 --> 03:26:07,625 and maybe that student is named Harry and that student's house, 2950 03:26:07,625 --> 03:26:10,041 for instance, might be Gryffindor. 2951 03:26:10,041 --> 03:26:14,750 And meanwhile, I might do something like this. professor = Professor over here. 2952 03:26:14,750 --> 03:26:18,541 And notice, the lowercase S on the left, capital S on the right. 2953 03:26:18,541 --> 03:26:21,875 Same for professor on the left-- lowercase and uppercase on the right 2954 03:26:21,875 --> 03:26:22,750 respectively. 2955 03:26:22,750 --> 03:26:25,625 Professor, quote, unquote, "Severus," and how 2956 03:26:25,625 --> 03:26:30,500 about Defense Against the Dark Arts will be his subject? 2957 03:26:30,500 --> 03:26:32,791 And meanwhile, if we want, more generically, 2958 03:26:32,791 --> 03:26:37,291 just a wizard, who, at the moment is neither student nor professor 2959 03:26:37,291 --> 03:26:39,375 teaching classes actively, we could even do that. 2960 03:26:39,375 --> 03:26:43,458 We could do wizard = Wizard in capital W on the right-hand side 2961 03:26:43,458 --> 03:26:45,708 of the equal sign, because it's the name of the class. 2962 03:26:45,708 --> 03:26:47,166 And someone like Albus-- 2963 03:26:47,166 --> 03:26:49,958 passing in only Albus's name-- 2964 03:26:49,958 --> 03:26:55,708 not a house, not a subject, because, in this case, he's known only as a wizard. 2965 03:26:55,708 --> 03:26:58,750 Meanwhile, with each of these calls, this line of code 2966 03:26:58,750 --> 03:27:01,833 here will ensure that the init method for the wizard class is called. 2967 03:27:01,833 --> 03:27:05,583 This line of code here will ensure that the init method of the student 2968 03:27:05,583 --> 03:27:09,250 class and, in turn, the init method of the superclass wizard is called. 2969 03:27:09,250 --> 03:27:11,541 And then lastly, on this final line of code, 2970 03:27:11,541 --> 03:27:15,625 will this syntax ensure that the init method of the professor class 2971 03:27:15,625 --> 03:27:20,375 is called, which, in turn, calls the init method of the superclass as well. 2972 03:27:20,375 --> 03:27:24,083 Any questions now on this idea of inheritance, 2973 03:27:24,083 --> 03:27:29,125 which is a key feature of a lot of object-oriented programming languages? 2974 03:27:29,125 --> 03:27:33,458 2975 03:27:33,458 --> 03:27:36,708 MICHAEL: From what I've seen so far, a lot of times, there's a lot of nesting. 2976 03:27:36,708 --> 03:27:38,375 If you do super, does it go one up? 2977 03:27:38,375 --> 03:27:44,375 Is there any situation where it's nested in another class as well, above Wizard, 2978 03:27:44,375 --> 03:27:44,875 let's say? 2979 03:27:44,875 --> 03:27:46,500 DAVID J. MALAN: A really good question. 2980 03:27:46,500 --> 03:27:48,375 If you were to have a super superclass-- so 2981 03:27:48,375 --> 03:27:52,833 your hierarchy is even taller than the two levels of hierarchy 2982 03:27:52,833 --> 03:27:56,625 that we currently have, absolutely. 2983 03:27:56,625 --> 03:27:59,250 What's nice about inheritance, as the name implies, is, 2984 03:27:59,250 --> 03:28:02,583 just as you might have inherited certain traits as a human 2985 03:28:02,583 --> 03:28:06,000 from your grandfather and grandmother or your great-grandfather 2986 03:28:06,000 --> 03:28:08,333 or great-grandmother, some of those properties 2987 03:28:08,333 --> 03:28:13,083 can actually trickle down to you in the context of code as well. 2988 03:28:13,083 --> 03:28:17,291 So when you descend from another class-- 2989 03:28:17,291 --> 03:28:21,541 that is, when you subclass a superclass or a super superclass, 2990 03:28:21,541 --> 03:28:23,958 you actually do inherit all of the functionality, 2991 03:28:23,958 --> 03:28:26,625 not just from one level above you but from two or three, 2992 03:28:26,625 --> 03:28:29,583 so you can indeed access some of that functionality as well. 2993 03:28:29,583 --> 03:28:32,666 And you can even override it if you want some of these classes 2994 03:28:32,666 --> 03:28:35,750 to behave a little bit differently than others. 2995 03:28:35,750 --> 03:28:38,125 Other questions on inheritance. 2996 03:28:38,125 --> 03:28:39,875 AUDIENCE: So it's similar to the last one, 2997 03:28:39,875 --> 03:28:43,125 but can you have two parents on the same level? 2998 03:28:43,125 --> 03:28:44,750 DAVID J. MALAN: A really good question. 2999 03:28:44,750 --> 03:28:49,875 So there are ways to implement descendants from multiple parents. 3000 03:28:49,875 --> 03:28:53,125 And there's different ways to do this, not just in Python but other languages. 3001 03:28:53,125 --> 03:28:57,916 We've kept things simple here, though, by having a single inheritance path. 3002 03:28:57,916 --> 03:28:58,666 A good question. 3003 03:28:58,666 --> 03:29:01,291 How about one more question on inheritance? 3004 03:29:01,291 --> 03:29:07,208 AUDIENCE: Can we have multiple arguments in super.__init? 3005 03:29:07,208 --> 03:29:11,000 DAVID J. MALAN: Yes, but in this case, I'm only passing a name on line 18, 3006 03:29:11,000 --> 03:29:13,666 and I'm only passing in name on line 10. 3007 03:29:13,666 --> 03:29:14,166 Why? 3008 03:29:14,166 --> 03:29:18,208 Because, on line 2, when I define the init method for the Wizard class, 3009 03:29:18,208 --> 03:29:20,375 I only expect a single argument. 3010 03:29:20,375 --> 03:29:23,583 But I could absolutely have other common functionality. 3011 03:29:23,583 --> 03:29:24,750 I could add in a patronus. 3012 03:29:24,750 --> 03:29:27,125 If both students and professors have patronuses 3013 03:29:27,125 --> 03:29:30,958 that can come out of their wands, I could have two arguments instead. 3014 03:29:30,958 --> 03:29:33,708 We've been using this feature of object-oriented programming 3015 03:29:33,708 --> 03:29:37,000 now for quite some time in the form of exceptions. 3016 03:29:37,000 --> 03:29:40,625 Indeed, if you look at the official documentation for exceptions in Python, 3017 03:29:40,625 --> 03:29:43,208 you'll see that there's not even the ones we've seen in class, 3018 03:29:43,208 --> 03:29:44,541 like value error and others. 3019 03:29:44,541 --> 03:29:48,666 There's any number of others as well, but they are all, themselves, 3020 03:29:48,666 --> 03:29:50,125 hierarchical in nature. 3021 03:29:50,125 --> 03:29:54,416 This is just a subset of the available exceptions that come built into Python. 3022 03:29:54,416 --> 03:29:58,125 And you can actually, as a programmer, create your own exceptions as well. 3023 03:29:58,125 --> 03:30:01,916 But as this chart here captures hierarchically, 3024 03:30:01,916 --> 03:30:06,125 all exceptions we've seen thus far actually descend from 3025 03:30:06,125 --> 03:30:09,041 or inherit from superclasses already. 3026 03:30:09,041 --> 03:30:10,833 So for instance, at the bottom of this list 3027 03:30:10,833 --> 03:30:13,250 here is ValueError, which we've seen quite a bit. 3028 03:30:13,250 --> 03:30:17,583 And if you follow the line straight up on this ascii rendition of this chart, 3029 03:30:17,583 --> 03:30:22,083 you'll see that ValueError has a parent, class, or superclass, called exception. 3030 03:30:22,083 --> 03:30:25,958 And the exception class, meanwhile, has a parent class called base exception. 3031 03:30:25,958 --> 03:30:28,125 Why did the authors of Python do this? 3032 03:30:28,125 --> 03:30:32,958 Well, it turns out that, whether you have a value error or a key error 3033 03:30:32,958 --> 03:30:35,416 or an assertion error or any number of others, 3034 03:30:35,416 --> 03:30:40,250 there's a lot of functionality common to all of those types of errors 3035 03:30:40,250 --> 03:30:42,458 that you want-- 3036 03:30:42,458 --> 03:30:44,958 that you want a programmer to be able to use. 3037 03:30:44,958 --> 03:30:48,375 And so it turns out that the authors of Python decided, you know what? 3038 03:30:48,375 --> 03:30:51,791 Let's not have a dozen or more different classes 3039 03:30:51,791 --> 03:30:55,166 that all just have copy, pasted similar functionality. 3040 03:30:55,166 --> 03:30:57,250 Let's create this hierarchy so that, even 3041 03:30:57,250 --> 03:31:00,875 though the exceptions toward the bottom of this list are very precise, 3042 03:31:00,875 --> 03:31:02,291 they at least inherit-- 3043 03:31:02,291 --> 03:31:05,583 that is, borrow some very common functionality up above. 3044 03:31:05,583 --> 03:31:09,541 So it turns out that, when you use the Try and the Accept keyword in Python, 3045 03:31:09,541 --> 03:31:13,291 generally speaking, we've tried to catch very specific exceptions, 3046 03:31:13,291 --> 03:31:14,291 like ValueError. 3047 03:31:14,291 --> 03:31:17,458 But technically, you could capture the parents or even 3048 03:31:17,458 --> 03:31:20,375 the grandparent exception for a given exception, 3049 03:31:20,375 --> 03:31:23,500 especially if you're not necessarily sure which one is going to get raised. 3050 03:31:23,500 --> 03:31:27,041 Or, better yet, there could be many exceptions that get raised, 3051 03:31:27,041 --> 03:31:28,958 but you want to handle them all the same, 3052 03:31:28,958 --> 03:31:31,000 and you don't want to necessarily enumerate them 3053 03:31:31,000 --> 03:31:33,041 in parentheses, separated by commas. 3054 03:31:33,041 --> 03:31:36,625 You want to say you want to handle all exceptions of a certain superclass 3055 03:31:36,625 --> 03:31:38,416 in much the same way. 3056 03:31:38,416 --> 03:31:40,833 So this has been latent this whole time, any time we've 3057 03:31:40,833 --> 03:31:45,166 seen or used or caught or, now, raised exceptions, and built into Python 3058 03:31:45,166 --> 03:31:46,041 is this hierarchy. 3059 03:31:46,041 --> 03:31:47,916 And if you were to invent your own exception, 3060 03:31:47,916 --> 03:31:50,250 generally, you wouldn't want to start from scratch. 3061 03:31:50,250 --> 03:31:54,041 You would want to descend from-- that is, subclass, one of these existing 3062 03:31:54,041 --> 03:31:58,958 exceptions and add your own twist on it, your own functionality as well. 3063 03:31:58,958 --> 03:32:02,208 Well, there's one final feature of object oriented programming 3064 03:32:02,208 --> 03:32:04,750 that we'd like to share with you today, and then it 3065 03:32:04,750 --> 03:32:08,291 will perhaps be quite the eye opener as to what you can really do now 3066 03:32:08,291 --> 03:32:10,208 that you have classes at your disposal. 3067 03:32:10,208 --> 03:32:12,791 And this, too, surprise, has been a feature 3068 03:32:12,791 --> 03:32:15,458 you and I have been taking for granted for weeks now. 3069 03:32:15,458 --> 03:32:18,250 This has just worked, but it's been implemented in a way 3070 03:32:18,250 --> 03:32:20,208 that you can now leverage yourself. 3071 03:32:20,208 --> 03:32:23,166 It turns out that Python, and some other languages, too, 3072 03:32:23,166 --> 03:32:27,041 support this notion of operator overloading, whereby 3073 03:32:27,041 --> 03:32:32,250 you can take very common symbols, like plus or minus or other such syntax 3074 03:32:32,250 --> 03:32:37,416 on the keyboard, and you can implement your own interpretation thereof. 3075 03:32:37,416 --> 03:32:40,791 Plus does not have to equal addition. 3076 03:32:40,791 --> 03:32:43,291 And minus does not have to equal subtraction. 3077 03:32:43,291 --> 03:32:45,708 And in fact, you and I have already seen another context 3078 03:32:45,708 --> 03:32:48,541 in which plus means something else. 3079 03:32:48,541 --> 03:32:52,750 Plus has not always, in Python, meant addition, per se. 3080 03:32:52,750 --> 03:32:56,458 What else has Python used plus for? 3081 03:32:56,458 --> 03:32:57,458 AUDIENCE: Concatenation? 3082 03:32:57,458 --> 03:32:58,875 DAVID J. MALAN: For concatenation. 3083 03:32:58,875 --> 03:33:02,875 For joining two strings, for adding to a list can you use plus as well. 3084 03:33:02,875 --> 03:33:07,666 So plus has actually been, funny enough, overloaded by the authors of Python 3085 03:33:07,666 --> 03:33:08,541 for us. 3086 03:33:08,541 --> 03:33:12,000 And so we can use the same symbol in much the same way as addition 3087 03:33:12,000 --> 03:33:15,250 but with different data types to solve slightly different problems. 3088 03:33:15,250 --> 03:33:18,250 Well, let me propose that we go back over to VS Code here, 3089 03:33:18,250 --> 03:33:22,250 and let me go ahead and create a new final file called vault.py. 3090 03:33:22,250 --> 03:33:24,000 So code of vault.py. 3091 03:33:24,000 --> 03:33:27,416 And let me propose that we implement the idea of a vault 3092 03:33:27,416 --> 03:33:30,541 at Gringotts, keeping on theme, wherein there's 3093 03:33:30,541 --> 03:33:32,291 a bank in the world of Harry Potter. 3094 03:33:32,291 --> 03:33:35,083 And within this bank, families and individuals 3095 03:33:35,083 --> 03:33:38,958 have vaults containing all sorts of money in the wizarding world. 3096 03:33:38,958 --> 03:33:41,583 And the type of money that exists in the world of Harry Potter 3097 03:33:41,583 --> 03:33:44,791 are coins called galleons and sickles and Knuts, 3098 03:33:44,791 --> 03:33:47,083 and those are in descending order of value. 3099 03:33:47,083 --> 03:33:49,750 And so inside of a vault might be a whole bunch of coins-- 3100 03:33:49,750 --> 03:33:53,000 gold, silver, and bronze, essentially, each in those denominations, 3101 03:33:53,000 --> 03:33:53,833 tucked away. 3102 03:33:53,833 --> 03:33:58,083 So how can I go about implementing, first of all, the idea of a vault 3103 03:33:58,083 --> 03:34:02,375 so that I can store, for instance, for Harry Potter, how much coinage 3104 03:34:02,375 --> 03:34:05,916 is in his family's vault, or for Ron Weasley the same? 3105 03:34:05,916 --> 03:34:08,291 Well, let me go ahead and vault.py and first 3106 03:34:08,291 --> 03:34:12,291 create a class called Vault, essentially meant to represent a bank vault. 3107 03:34:12,291 --> 03:34:15,000 Perfect, another real world, or fantasy world, 3108 03:34:15,000 --> 03:34:17,500 entity that I want to represent with code. 3109 03:34:17,500 --> 03:34:19,958 I could use a tuple or a list or a dictionary. 3110 03:34:19,958 --> 03:34:23,083 But again, I'm going to get a lot more functionality with classes, 3111 03:34:23,083 --> 03:34:26,125 and we'll see one final flourish with operators. 3112 03:34:26,125 --> 03:34:28,875 Inside of this vault class, let's go ahead and do this. 3113 03:34:28,875 --> 03:34:32,625 Let me define my init method, taking its first argument of self. 3114 03:34:32,625 --> 03:34:35,625 And let me define three arguments to this. 3115 03:34:35,625 --> 03:34:38,541 When you create a vault, in my code here, 3116 03:34:38,541 --> 03:34:41,750 I want to be able to initialize it with some number of galleons, some number 3117 03:34:41,750 --> 03:34:43,375 of sickles and, some number of Knuts. 3118 03:34:43,375 --> 03:34:46,791 I want the user, the programmer, to be able to pass in one or more 3119 03:34:46,791 --> 03:34:47,833 of those values ideally. 3120 03:34:47,833 --> 03:34:50,083 But they can be optional, so I'll give them defaults. 3121 03:34:50,083 --> 03:34:53,000 So let's go ahead and define a parameter called galleons, 3122 03:34:53,000 --> 03:34:57,041 whose default value will be 0; sickles, whose default value will also be 0; 3123 03:34:57,041 --> 03:35:00,083 and knuts, whose default value will be 0 as well. 3124 03:35:00,083 --> 03:35:04,541 So the programmer can pass in one or two or three or even none of those, 3125 03:35:04,541 --> 03:35:07,125 and they'll all have some implied defaults. 3126 03:35:07,125 --> 03:35:10,500 How do I want to remember those values that are passed in? 3127 03:35:10,500 --> 03:35:11,541 Well, let me do this. 3128 03:35:11,541 --> 03:35:14,583 self.galleons = galleons. 3129 03:35:14,583 --> 03:35:17,416 And self.sickles = sickles. 3130 03:35:17,416 --> 03:35:20,708 And self.knuts = knuts. 3131 03:35:20,708 --> 03:35:24,541 And so I could add some error checking, especially if you don't pass in. 3132 03:35:24,541 --> 03:35:27,708 A number I could turn these into properties to do even more validation. 3133 03:35:27,708 --> 03:35:30,791 But let's keep it simple and, as always, focus only on the new ideas. 3134 03:35:30,791 --> 03:35:33,291 So I'm just going to trust that these values were passed in, 3135 03:35:33,291 --> 03:35:36,708 and I'm going to immediately assign them to these instance variables. 3136 03:35:36,708 --> 03:35:38,416 What, now, do I want to do? 3137 03:35:38,416 --> 03:35:41,208 Well, let's come up with a way of printing out 3138 03:35:41,208 --> 03:35:44,000 what is in someone's vault, ultimately. 3139 03:35:44,000 --> 03:35:45,291 But first let's do this. 3140 03:35:45,291 --> 03:35:50,083 Let's create a vault for the Potters by creating, via assignment, a new vault. 3141 03:35:50,083 --> 03:35:55,125 And let's say that the potters have 100 galleons, 50 sickles, and 24 knuts. 3142 03:35:55,125 --> 03:35:58,541 And that's in that vault. And let's print out, for instance, potter. 3143 03:35:58,541 --> 03:36:00,916 All right, let's run this code and see how it works now. 3144 03:36:00,916 --> 03:36:05,083 Let me go ahead and run Python of vault.py, Enter. 3145 03:36:05,083 --> 03:36:06,083 Seems to work. 3146 03:36:06,083 --> 03:36:08,041 No syntax errors or anything else. 3147 03:36:08,041 --> 03:36:10,791 But this is not very enlightening. 3148 03:36:10,791 --> 03:36:15,291 How do I fix this, thinking back to what we've done before? 3149 03:36:15,291 --> 03:36:17,958 AUDIENCE: You have to use the __str. 3150 03:36:17,958 --> 03:36:18,958 DAVID J. MALAN: Exactly. 3151 03:36:18,958 --> 03:36:22,291 I need to use one of those special methods that comes with classes 3152 03:36:22,291 --> 03:36:26,625 and define for myself how I want a vault to be printed as a string. 3153 03:36:26,625 --> 03:36:27,916 So let me go ahead and do that. 3154 03:36:27,916 --> 03:36:32,541 Let me define the str method taking in self as its sole argument here. 3155 03:36:32,541 --> 03:36:34,500 And let's just return a very simple string 3156 03:36:34,500 --> 03:36:36,833 that just reveals what's in the vault. 3157 03:36:36,833 --> 03:36:39,958 So I'm going to return a formatted f string, 3158 03:36:39,958 --> 03:36:44,166 inside of which is self.galleons and then the word galleon, 3159 03:36:44,166 --> 03:36:45,875 so I know which those are. 3160 03:36:45,875 --> 03:36:49,458 Then let's do self.sickles, and let's output the word, sickles. 3161 03:36:49,458 --> 03:36:53,083 And then lastly let's output self.knuts, and then knuts here. 3162 03:36:53,083 --> 03:36:58,041 So I know, in this string, just how many of each of those coins 3163 03:36:58,041 --> 03:37:01,666 I have in this particular family's vault. All right, let me go ahead 3164 03:37:01,666 --> 03:37:06,166 and run Python of vault.py, changing nothing else except the str method. 3165 03:37:06,166 --> 03:37:11,625 And now, , we, see indeed that Harry has 100 galleons, 50 sickles, and 25 knuts. 3166 03:37:11,625 --> 03:37:13,541 All right, well, let's do one thing more here. 3167 03:37:13,541 --> 03:37:16,875 Below that, let's go ahead and define a Weasley variable. 3168 03:37:16,875 --> 03:37:20,583 And Ron never seemed to have quite as much money in the vault as did Harry. 3169 03:37:20,583 --> 03:37:24,541 So let's say that the Weasley vault will have 25, 50, and 100. 3170 03:37:24,541 --> 03:37:27,041 So I'll just reverse the order of those denominations, 3171 03:37:27,041 --> 03:37:29,541 rather than Harry's 100, 50, 25. 3172 03:37:29,541 --> 03:37:33,125 And now let me go ahead and print Weasley like this. 3173 03:37:33,125 --> 03:37:37,291 And let's go ahead and clear my terminal window, run Python of vault.py. 3174 03:37:37,291 --> 03:37:40,541 This time, that str method will be invoked twice, once 3175 03:37:40,541 --> 03:37:42,416 for each of those vault objects. 3176 03:37:42,416 --> 03:37:44,666 And we'll see, indeed, that the first one for Harry 3177 03:37:44,666 --> 03:37:48,125 has got 100, 50, and 25, respectively, versus Ron's 25, 50, 3178 03:37:48,125 --> 03:37:50,583 and 100, respectively. 3179 03:37:50,583 --> 03:37:52,458 But now let's do something interesting. 3180 03:37:52,458 --> 03:37:56,333 Suppose that you wanted to combine the contents of two vaults, 3181 03:37:56,333 --> 03:37:58,875 be it Harry's and Ron's or any other two people. 3182 03:37:58,875 --> 03:38:02,083 How would you go about doing this in code? 3183 03:38:02,083 --> 03:38:06,000 Well, if I wanted to combine the vaults for someone, I could do this. 3184 03:38:06,000 --> 03:38:08,333 Well, I could do galleons equals-- 3185 03:38:08,333 --> 03:38:13,958 let's do potter.galleons + weasley.galleons. 3186 03:38:13,958 --> 03:38:16,250 That gives me a variable called galleons that has 3187 03:38:16,250 --> 03:38:19,041 the sum of Harry and Ron's galleons. 3188 03:38:19,041 --> 03:38:25,333 Let's next do sickles = potter.sickles + weasley.sickles. 3189 03:38:25,333 --> 03:38:31,916 And then lastly, let's do knuts = potter.knuts + weasley.knuts. 3190 03:38:31,916 --> 03:38:32,958 I've got three variables. 3191 03:38:32,958 --> 03:38:34,666 What can I now do with these values? 3192 03:38:34,666 --> 03:38:37,833 Well, let's create a third-- a new vault. Total will 3193 03:38:37,833 --> 03:38:41,416 be the name of this variable equals a new vault, Capital V, notice. 3194 03:38:41,416 --> 03:38:43,916 And now, let's pass in those three new variables-- 3195 03:38:43,916 --> 03:38:47,000 galleons, sickles, and knuts. 3196 03:38:47,000 --> 03:38:49,625 And that's it, and let's print out this total vault. 3197 03:38:49,625 --> 03:38:52,750 So we should now see three vaults-- one for Harry, for Ron, 3198 03:38:52,750 --> 03:38:55,208 and the combination-- the addition of the two. 3199 03:38:55,208 --> 03:38:58,875 Let me go ahead and rerun Python of vault.py, and there we have it. 3200 03:38:58,875 --> 03:39:04,875 What was 100, 50, 25 and 25, 50, and 100, combined through addition now, 3201 03:39:04,875 --> 03:39:08,250 is 125, 100, 125. 3202 03:39:08,250 --> 03:39:10,541 So pretty straightforward, using techniques from weeks 3203 03:39:10,541 --> 03:39:14,541 ago, where we're just declaring a few new variables and doing some addition. 3204 03:39:14,541 --> 03:39:18,208 But wouldn't it be cool if I could do something like this? 3205 03:39:18,208 --> 03:39:20,541 Wouldn't it be cool if I could just somehow, 3206 03:39:20,541 --> 03:39:24,541 not manually create my own vault and do all of this annoying math up here-- 3207 03:39:24,541 --> 03:39:30,541 what if I could just do potter + weasley and get rid of all of this logic here? 3208 03:39:30,541 --> 03:39:34,750 Wouldn't it be nice if I overload the operator-- 3209 03:39:34,750 --> 03:39:37,666 we know as plus, just like str does, just 3210 03:39:37,666 --> 03:39:42,041 like list does-- to allow me to add two vaults together 3211 03:39:42,041 --> 03:39:43,875 on the left and the right. 3212 03:39:43,875 --> 03:39:48,250 Well, it turns out in Python and through operator overloading, 3213 03:39:48,250 --> 03:39:50,250 there is a way to do just this. 3214 03:39:50,250 --> 03:39:52,708 If you consult the documentation, there's 3215 03:39:52,708 --> 03:39:56,750 this and so many other special methods that come with classes. 3216 03:39:56,750 --> 03:39:59,916 The third one we'll see here is this one here-- 3217 03:39:59,916 --> 03:40:03,291 __add__. 3218 03:40:03,291 --> 03:40:05,458 And you'll see that it very generically is described 3219 03:40:05,458 --> 03:40:09,333 in the documentation is working for any object, be it a vault or str 3220 03:40:09,333 --> 03:40:10,791 or a list or something else. 3221 03:40:10,791 --> 03:40:13,458 By convention, it's going to take a first argument called self, 3222 03:40:13,458 --> 03:40:17,083 and then it's going to take some other argument, by convention, called other. 3223 03:40:17,083 --> 03:40:20,083 self, in effect, is going to be referring to whatever 3224 03:40:20,083 --> 03:40:22,250 object is on the left of a plus sign. 3225 03:40:22,250 --> 03:40:24,166 other is going to be referring to whatever 3226 03:40:24,166 --> 03:40:26,291 is on the right-hand side of a plus sign, 3227 03:40:26,291 --> 03:40:30,333 thereby giving us a way of describing, in code, the operand 3228 03:40:30,333 --> 03:40:35,416 on the left and the operand on the right of the operator, plus, in between. 3229 03:40:35,416 --> 03:40:37,958 That is to say, if I go back to VS Code here, 3230 03:40:37,958 --> 03:40:42,083 what I'm trying to do is implement support for this. 3231 03:40:42,083 --> 03:40:45,041 Well, let me try, without writing any other code just yet-- 3232 03:40:45,041 --> 03:40:47,541 Python of vault.py, Enter-- 3233 03:40:47,541 --> 03:40:53,541 TypeError: unsupported operand type(s) for +: 'Vault' and 'vault.' 3234 03:40:53,541 --> 03:40:56,750 That is to say Python, at this moment, does not know what 3235 03:40:56,750 --> 03:40:59,083 it means to add two vaults together. 3236 03:40:59,083 --> 03:41:00,500 You and I might have an instinct. 3237 03:41:00,500 --> 03:41:03,958 Probably want to combine the galleons and the sickles and the knuts 3238 03:41:03,958 --> 03:41:04,791 respectively. 3239 03:41:04,791 --> 03:41:05,958 Python doesn't know that. 3240 03:41:05,958 --> 03:41:08,291 It just knows that you have a new class called Vault. 3241 03:41:08,291 --> 03:41:10,250 But let's teach Python to do this. 3242 03:41:10,250 --> 03:41:12,166 Let me clear my terminal window. 3243 03:41:12,166 --> 03:41:15,208 Let me scroll back up to the class itself, where, at the moment, 3244 03:41:15,208 --> 03:41:18,250 I only have two special methods-- init and str. 3245 03:41:18,250 --> 03:41:20,458 But let's add this third. 3246 03:41:20,458 --> 03:41:26,625 Let me go into the class here and define __add__ and then specify its first 3247 03:41:26,625 --> 03:41:30,791 parameter as self, as before, and then a second parameter for this particular 3248 03:41:30,791 --> 03:41:32,583 method called, by convention, other. 3249 03:41:32,583 --> 03:41:34,833 Now, as always, I could name those parameters anything 3250 03:41:34,833 --> 03:41:37,000 I want, but I'm going to stick with convention here. 3251 03:41:37,000 --> 03:41:39,041 And now, inside of this method, am I going 3252 03:41:39,041 --> 03:41:42,375 to have to now add together the contents of two vaults? 3253 03:41:42,375 --> 03:41:43,333 Well, what two vaults? 3254 03:41:43,333 --> 03:41:46,291 Well, if we scroll down to our goal at hand, the goal, of course, 3255 03:41:46,291 --> 03:41:48,583 is to add this vault plus this other vault-- potter 3256 03:41:48,583 --> 03:41:50,208 plus weasley, respectively. 3257 03:41:50,208 --> 03:41:53,958 Well, it turns out, in Python, that, when you do overload an operator like 3258 03:41:53,958 --> 03:41:56,291 plus, what's going to happen automatically, 3259 03:41:56,291 --> 03:42:01,666 as soon as Python sees that, is it's going to call that __add__ method, 3260 03:42:01,666 --> 03:42:04,041 and it's going to pass into it to arguments-- 3261 03:42:04,041 --> 03:42:07,333 whatever the operand is on the left-- potter, in this case-- 3262 03:42:07,333 --> 03:42:10,666 and whatever the operand is on the right-- weasley, in this case. 3263 03:42:10,666 --> 03:42:15,500 And those values are going to get passed in as self and other, respectively. 3264 03:42:15,500 --> 03:42:18,375 What that means is that we can access their contents up here 3265 03:42:18,375 --> 03:42:20,458 in our implementation of add as follows. 3266 03:42:20,458 --> 03:42:24,125 Let me go ahead and define a local variable called galleons and set that 3267 03:42:24,125 --> 03:42:27,500 equal to, for instance, the sum of self.galleons-- 3268 03:42:27,500 --> 03:42:30,125 whatever's in Potter's vault in this case, 3269 03:42:30,125 --> 03:42:33,041 plus whatever is in Wesley's vault in this case, which 3270 03:42:33,041 --> 03:42:35,000 would be other.galleons. 3271 03:42:35,000 --> 03:42:36,708 Let me do the same for sickles. 3272 03:42:36,708 --> 03:42:40,125 self.sickles + other.sickles. 3273 03:42:40,125 --> 03:42:41,958 And let me lastly do that for knuts. 3274 03:42:41,958 --> 03:42:45,791 So self.knuts + other.knuts. 3275 03:42:45,791 --> 03:42:47,625 But at the end of the day, I'm going to need 3276 03:42:47,625 --> 03:42:51,500 to return a brand new bigger vault that contains all of those contents 3277 03:42:51,500 --> 03:42:52,250 together. 3278 03:42:52,250 --> 03:42:56,791 And if we ultimately want to assign that bigger vault to a variable like total 3279 03:42:56,791 --> 03:43:01,083 here, on the left, we'd better return a value from this add method. 3280 03:43:01,083 --> 03:43:04,791 So I'm going to go ahead and give myself a brand new vault, as by returning 3281 03:43:04,791 --> 03:43:08,916 capital Vault, which of course, is going to call my vault function into which 3282 03:43:08,916 --> 03:43:11,750 I can now pass some of those initialization arguments. 3283 03:43:11,750 --> 03:43:13,625 Well, how many galleon, sickles, and knuts do 3284 03:43:13,625 --> 03:43:15,541 I want this brand new vault to contain? 3285 03:43:15,541 --> 03:43:19,666 Well, I want it to contain this many galleons this many sickles, 3286 03:43:19,666 --> 03:43:21,291 and this many knuts. 3287 03:43:21,291 --> 03:43:23,708 So ultimately, what we're doing in this implementation 3288 03:43:23,708 --> 03:43:27,791 of add is adding together those galleons, sickles, and knuts, passing 3289 03:43:27,791 --> 03:43:30,958 them to the vault function so that we get a brand new bigger vault, 3290 03:43:30,958 --> 03:43:33,833 and return that altogether. 3291 03:43:33,833 --> 03:43:36,041 So now I've defined this new special method 3292 03:43:36,041 --> 03:43:42,250 called add that should now just make plus work for two vaults. 3293 03:43:42,250 --> 03:43:42,833 Let's see. 3294 03:43:42,833 --> 03:43:46,750 Let me run down to my terminal window, Python of vault.py and hit Enter. 3295 03:43:46,750 --> 03:43:51,958 And voila, and now we've implemented an overloaded operator, plus, 3296 03:43:51,958 --> 03:43:54,166 to do what you and I as humans would hope 3297 03:43:54,166 --> 03:43:56,458 would be the case when you add two vaults together. 3298 03:43:56,458 --> 03:43:58,916 But I've now written the code more specifically 3299 03:43:58,916 --> 03:44:04,500 to teach Python what it means concretely to add two vaults together. 3300 03:44:04,500 --> 03:44:06,708 And it's with very similar code in effect, 3301 03:44:06,708 --> 03:44:09,541 underneath the hood, that Python is doing this for two strings, 3302 03:44:09,541 --> 03:44:13,875 to concatenate them together, to joining two lists into a new list with list, 3303 03:44:13,875 --> 03:44:16,875 and so many other classes as well. 3304 03:44:16,875 --> 03:44:22,166 Any questions now on operator overloading or this example here. 3305 03:44:22,166 --> 03:44:23,875 AUDIENCE: How would you go about creating 3306 03:44:23,875 --> 03:44:31,833 a function for adding a student and a vault for two separate classes? 3307 03:44:31,833 --> 03:44:33,250 Would that be possible? 3308 03:44:33,250 --> 03:44:35,125 DAVID J. MALAN: Let me see what happens here. 3309 03:44:35,125 --> 03:44:36,041 I don't know offhand. 3310 03:44:36,041 --> 03:44:36,875 Let's do this. 3311 03:44:36,875 --> 03:44:38,541 Let's create a str and see what happens. 3312 03:44:38,541 --> 03:44:40,875 If I add Potter plus a str-- 3313 03:44:40,875 --> 03:44:41,500 str object. 3314 03:44:41,500 --> 03:44:42,666 Yeah, so it would work. 3315 03:44:42,666 --> 03:44:44,708 I'm just figuring this out as I go here, Eric. 3316 03:44:44,708 --> 03:44:47,000 So just to be clear, what I did was I just 3317 03:44:47,000 --> 03:44:48,958 changed weasley to str just to see what would 3318 03:44:48,958 --> 03:44:54,166 happen when I add a vault plus a str, and it will work, theoretically. 3319 03:44:54,166 --> 03:44:54,791 Why? 3320 03:44:54,791 --> 03:45:01,541 Because so long as the type of value on the left has an add method implemented, 3321 03:45:01,541 --> 03:45:04,291 other can be any type that you want. 3322 03:45:04,291 --> 03:45:06,583 You just have to decide and code what it's 3323 03:45:06,583 --> 03:45:09,625 going to mean conceptually to add a vault plus a string, which, 3324 03:45:09,625 --> 03:45:13,458 in this case, probably doesn't make any sense at all, but it's possible. 3325 03:45:13,458 --> 03:45:15,208 It's going to be the operand on the left. 3326 03:45:15,208 --> 03:45:16,250 And I'm inferring that. 3327 03:45:16,250 --> 03:45:17,958 I did not know the answer a moment ago. 3328 03:45:17,958 --> 03:45:22,916 I'm inferring that because what I got was an attribute error here on line 11 3329 03:45:22,916 --> 03:45:27,125 because Python did not like this. other.galleons didn't work, 3330 03:45:27,125 --> 03:45:29,666 but I could make it work by figuring something out. 3331 03:45:29,666 --> 03:45:30,791 Really good question. 3332 03:45:30,791 --> 03:45:32,291 Didn't know that one myself. 3333 03:45:32,291 --> 03:45:35,916 Other questions on operator overloading? 3334 03:45:35,916 --> 03:45:38,291 AUDIENCE: Can you define new operators in Python? 3335 03:45:38,291 --> 03:45:39,666 DAVID J. MALAN: I don't think so. 3336 03:45:39,666 --> 03:45:44,583 There is a very long but precise list of operators that you can overload. 3337 03:45:44,583 --> 03:45:48,500 I do not believe you can assign arbitrary characters 3338 03:45:48,500 --> 03:45:50,541 to be operators in Python. 3339 03:45:50,541 --> 03:45:52,458 Let me defer to Carter in the chat to-- 3340 03:45:52,458 --> 03:45:55,166 OK, I'm seeing two of my colleagues are saying, no, not possible. 3341 03:45:55,166 --> 03:45:57,166 So I'm going to go with my first instinct, no. 3342 03:45:57,166 --> 03:45:58,583 Otherwise, that'd be kind of cool. 3343 03:45:58,583 --> 03:46:00,875 You could make emoji do whatever you want to. 3344 03:46:00,875 --> 03:46:04,000 How about one final question on operator overloading? 3345 03:46:04,000 --> 03:46:08,416 AUDIENCE: Is that the only operation you can do as far as-- 3346 03:46:08,416 --> 03:46:10,041 can you do a subtraction as well? 3347 03:46:10,041 --> 03:46:11,041 DAVID J. MALAN: You can. 3348 03:46:11,041 --> 03:46:13,583 You can do so many others let me. 3349 03:46:13,583 --> 03:46:16,583 If, Carter, you don't mind pulling up this URL here-- 3350 03:46:16,583 --> 03:46:19,750 so this link here-- special method names and today's slides, 3351 03:46:19,750 --> 03:46:23,208 you'll see a long list of all of the operators that you can overload. 3352 03:46:23,208 --> 03:46:26,583 You can do less than, equals than, plus equals, minus equals. 3353 03:46:26,583 --> 03:46:29,291 Pretty much any symbol you've seen me type on the screen 3354 03:46:29,291 --> 03:46:33,166 can be overloaded in the context of classes. 3355 03:46:33,166 --> 03:46:35,541 So even though, today, we focused entirely 3356 03:46:35,541 --> 03:46:38,625 on object-oriented programming, this is a technique that we've been using, 3357 03:46:38,625 --> 03:46:41,791 really, since the first week of the class because those ints, 3358 03:46:41,791 --> 03:46:45,291 those strs, those floats, those lists, those dictionaries, 3359 03:46:45,291 --> 03:46:49,083 and so much more were already underneath the hood this whole time-- classes 3360 03:46:49,083 --> 03:46:50,125 and objects thereof. 3361 03:46:50,125 --> 03:46:52,041 But you now, as a programmer, have the ability 3362 03:46:52,041 --> 03:46:55,583 to create your own classes with your own instance or class variables, 3363 03:46:55,583 --> 03:46:58,875 with your own instance or class methods, with your own properties, 3364 03:46:58,875 --> 03:47:01,958 and even with your own custom behavior for operators. 3365 03:47:01,958 --> 03:47:04,625 So ultimately, you can absolutely continue 3366 03:47:04,625 --> 03:47:09,291 using those simple tuples or lists or those dictionaries or other structures 3367 03:47:09,291 --> 03:47:09,791 as well. 3368 03:47:09,791 --> 03:47:13,416 But object-oriented programming, and with it, classes and now these objects 3369 03:47:13,416 --> 03:47:15,208 is just another tool in your toolkit. 3370 03:47:15,208 --> 03:47:18,041 And daresay, as your code gets more sophisticated 3371 03:47:18,041 --> 03:47:20,041 and your problems get bigger, you'll find 3372 03:47:20,041 --> 03:47:23,708 that being able to model these real world or even fantasy world entities 3373 03:47:23,708 --> 03:47:26,458 with classes and related data and functionality 3374 03:47:26,458 --> 03:47:30,583 will ultimately just allow you to define code that's not just correct but ever 3375 03:47:30,583 --> 03:47:32,833 well-designed as well. 3376 03:47:32,833 --> 03:47:35,541 This was CS50. 3377 03:47:35,541 --> 03:47:41,000