 Hello everyone. In the last video we talked about defining Python classes and also defining methods within those classes. And as we know now, Python classes are the way that Python implements data types. And methods are the equivalent of functions in a Python class, but those functions are actually like inside the class itself inside the data type. This lecture what we're going to do is get actually put the data in the data type. And the way that we do that is through what we call class instance variables. So let's get to it. Alright, so last time we were working with this class called student and we did a few very simplistic things with this class. Now suppose that we're building a system that is somehow being used to manage student accounts. Maybe it's a course registration system or heaven forbid, something to do with tuition processing. In systems like that, in a lot of data processing, data manipulation systems, but not just those systems. Also fun things like games and film editing, apps. You want to have the data, you start working with concepts, with types, right? You start working with the concept of a student, the concept of a game, the concept of a song. And so in our data type, in our example of where we were working with students, and let's say we want to have them registering for classes, how are we going to distinguish the students from one another? We're just talking abstractly here. How do you distinguish a student from one another? Well, students have different data associated with them, different names, different ID numbers, different birth dates, different phone numbers, etc. And so it's really helpful in software development in general and to group together related pieces of data. And that's kind of, you know, that's the premise of a data type. Strings in Python are collections of individual characters, lists are collections of elements, and even integers, right? Integers are collections of little tiny bits, ones and zeros that are used to represent a whole number. So, but we can make data types hold whatever data, low level, fundamental data we want. So suppose that we have 17,000 students in our system, that's about what we have here at UNCW right now. Before, all our very simple class did was it printed out the person's name, right? And so, well, okay, if we want to have 17,000 students and we have different students with different names for the most part, well, using our first approach where we were printing out my name is blah, we had student and another student, right, two different classes to print out two different names. Well, that's a little silly, right? If we wanted to do this with 17,000 different students, we'd have to have 17,000 different people, you know, different classes. Well, that's no good. And also, well, then we evolved a little bit and we created a method. We made it so that our say name method took a parameter. So, when you used the class, when you called say name, you could say, hey, call it with whatever value you wanted. Well, that's a little bit of an improvement, but still, if you wanted to have a student class and you wanted to say the name of 17,000 different students, you still need to know their names, right, because you've got to give the name to the student class. So that's not great. And it just makes more sense, right? What we want to have is the data associated with a person, with the big concept of a student, the real world concept of a student, we want that affiliated with the class. The way that we do that is through class, instance, variables, okay? So, there's, here's our student class and we're going to augment it a little bit. So, if you're following along in the code, I would, you know, pause the video for a second, make it so that your code looks like this, okay? All right. And now, let's explain what we, what we have done here. The first thing you'll notice is we've added this new method here, okay? So, this thing is a method, it's got, it looks like a function, but it has a very specific name, and this name is predetermined by Python. It is underscore, underscore, init, underscore, underscore, okay? So, these are two underscores crammed together, lower case init. This is a baked in thing in Python, you just have to memorize it, okay? This method is a very special method that is called the constructor. There are constructor methods in other programming languages as well. The job of the constructor is when you want to make a new student class, you need, or a new instance, this is the method that gets called implicitly by Python, okay? So, if you do not define the constructor method, and we didn't do that before, right? We didn't have this method before, and you call student, Python just gives you kind of like a blank student. Well, now what we want to do is we want to initialize some data in that student, and in order to do that, we need to define the constructor and give it the data we want to have inside the student, all right? So, this is like formulaic, you just got to know this, def init, and then this is a method, and it is a method, so its first parameter must be self. Now, I'm going to give it a second parameter, and that parameter is going to be n, and it's the name, okay? So, what happens inside the constructor, I have self.name gets n, all right? This self.name, self., right? What is self? Self refers to the current instance of the class. It's saying, put an instance variable, create a new variable called name, but it's on the whole class, okay? So that everything inside this class, including other methods, can refer to it, okay? So, this self.name instance variable, you have to have the self. here, but then you can call this part whatever you want, it could be name, it could be first name, it could be last name, it could be x, whatever. First part is self., and then the instance variable name, this is just a variable name, follows the same rules as other variable names, okay? So now, self.name, right? That self.name value, the attribute value is going to live inside my student instance for as long as the student instance exists in memory, okay? So at any time I can go to my student instance and access the name value and the name variable, right? So down here in say name, I've modified it a little bit. Instead of taking a parameter and instead of being hard coded, it's going to print out whatever the value of self.name is, okay? All the other methods inside this class can access instance variables by using self. the name of the variable, okay? So go ahead and run this. I'll go over to my code and I'll make it look like this real quick, right? So I'm going to define my constructor and then I gave it, now if you're in PyCharm, you'll notice PyCharm will like autocomplete this for you, pretty nice. Def, start by doing my underscore, underscore, and we'll look at this. There's a whole bunch of them and we'll talk about them a little bit next video. But the second one on my list is a knit. So if I hit the, you know, kind of scroll down, let me do it again. Kind of going down with my arrow keys, select it, hit enter, fills it in for me. Kind of nice. Autocompletes. Now on the slides I gave myself a second parameter and what I did was I did self.name, okay, creating my instance variable gets whatever value this thing has, right? This is just like a function definition. Whatever this parameter value is, I assign it here, okay? Now down here I'm going to change my method. It doesn't take a parameter anymore but what it's going to print out is self.name. All right, so let's go back to the slides for a second. So I've modified my student class now. I want to use it. I want to take advantage of this new structure. So note my line down here. Self the x gets, I want to use my new student class. Student, this calls the constructor, with an argument, the string Lucas, okay? So remember the constructor is a method. So it can take arguments, it can take parameters but you do not call underscore underscore init directly. This style right here, the name of the class followed by parentheses, automatically triggers the init method, okay? So you do not call underscore underscore init underscore underscore with parentheses. You call this like that, okay? And Python interprets it to say, oh okay, he wants a new student, go find its init method and call it, right? Python does that for you. So when I call the constructor with the argument Lucas, well what happens? Python goes up here and it looks for the constructor which it finds and it substitutes in n here for this argument, okay? So self I called it with the string Lucas, it says okay n is equal to Lucas, so assign to my class instance variable self.name the value of n which happens to be Lucas, okay? So if we go back to our code and kind of do this, I'm going to punch in my argument to my constructor. Now when I run this, right, I'll initialize, it's kind of a two-step process, I initialize a new instance of the student class with this data and then second step I wind up calling sayName and sayName is going to go in, find the value of self.name which I already initialized and it'll print it out. So let's run this. There it goes, right? My name is Lucas. All right, not too bad, right? But just kind of bear in mind, if you want to have, if your class is going to have data in it, you probably, you need, you should initialize the variables in the constructor and then you can refer to them in any of the other class methods, okay? All right, let's go back to our slides. All right, so now the student has data. We can make a variety multiple instances of students all with different data, right? So go ahead and punch this in, you know, student x, okay, x is a student whose data is Alice, y is a student whose data is Bob. Just like we have strings, you make a new string, x gets the string UNCW, y gets the string UNCG, right? Two different instances of the string class but with different data. However, they know the same operations as well, right? You know, students, whether you have data Alice or whether you're a student with the data Bob, both of you know how to say the name, okay? So now this is a little bit more like how people do real software design when they're thinking about data. Now what we will, we can do is we, if we're trying to build our say course registration system or our payroll system or our tuition system, what we would do is we would create different instances of students that all have different data, okay? So this is sort of what it looks like visually. Here's our class diagram. x is getting a student with the data Alice and y is getting a student with the data Bob. x and y are both instances of students. They look the same, right? They have an instance variable called name, self.name. They have two methods called init and sayName. But their data is different, right? That's what we have done here. We have created a data type called student that has some data value, but different operations, or excuse me, different data values. They're stored in the same place in the same name variable, but we're going to have different data values, but the same operations. Pretty cool, okay? The instance variables are have the same name, but different values, and that's kind of an important point. In fact it's so important why don't I try and make it explicit? The name here is Alice. The name here is Bob, okay? But they have the same variable names and they have the same methods. Very important, okay? So a couple big notes, big important things to remember about class instance variables. Instance variables are shared among all methods in the class, so any method can access an instance variable using that self dot, right? Self dot. Instance variables are always referenced with self dot when you're inside the class. And your instance variables should be initialized in the constructor. Technically they don't have to be, but it's best practice that you do. Okay? All right. It's absolutely possible to have a constructor with multiple parameters, right? And previously I just had the one, n. You can do multiple parameters just like you can with a function call. Same thing, right? What's it gonna look like? It's gonna look like this. Student x gets Alice, student Alice comma Jones. First comma last, right? This Alice matches to first, this Jones matches to last, right? Let's go over here and do this quickly, okay? Because I want to show you something else. All right, so I'm going to rename my instructor parameters to first and last. So I'll say self dot first name gets first. Self dot last name gets last. By the way, it's always a good idea to kind of be explicit in your variable names. Your variable names should try to capture what the information is that they store. And in Python, the convention is not to do like first name all mashed together. That's not good. And in Python you don't do this camel casing first big name. In Python the convention is to do this. It's not syntactically wrong to do it this way, but the convention from the Python gods who created the languages do it this way. Underscores put underscores in there, okay? All right, so let's modify our say name method so that it says my name is self dot first name. Actually you know what? Let's use an f string. We covered f strings early on. Let's this is a great time to use it. My name is put my little f here before the the string quote self dot first name and then what a space and then self dot last name. Okay, so PyCharm's telling me I need to format my things better. Now I want to call my constructor but I've got to give it a first name and a last name. If I don't, if I forget, if I forget to give it that all the parameters it needs, it's going to yell at me, right? Missing one required positional argument last. You forgot last, okay? Let's give my my own name, all right? And let's let's copy this. Let's make another student. Let's do why gets the student Sammy Seahawk and let's have why say his name like this, okay? Run it. My name is Lucas Layman. My name is Sammy Seahawk. All right, so cool. We got a student. It's got multiple at class instance variables first name and last name and we can initialize them in our constructor and then use them later on in another method. That's totally fine. Let's say you want to change the data on class on your student instance. By default any class that you define, any data type you define is a mutable data class, right? That means that its data values can be changed. Python though has classes with immutable data types like ints and strings and floats. Once you've created instances of an int to string a float, you actually can't change its data type or its data. It's stuck there. Our self-defined classes, you can change them. So how do you do that? How do you access and also how do you access these variable values when you're outside of the class? It's different. So self.firstname has no meaning down here. Say we just want to print out Sammy's first name. Well, self has no meaning out here. Self only exists in the context of a class. So how do we, let's say we want to print just Sammy Seahawk's first name. How do we do it? Well, the variable y is holding the reference to our student instance with Sammy Seahawk's data. So if I want to print Sammy Seahawk's first name, I have to refer to his instance of the student. In that case outside, right, when I'm outside student class definition, I need to use the variable that refers to Sammy. So in this case, y.firstname will go to the instance of the student and using my dot operator here, I can access the variable value inside the instance. Okay, so if I run this, the last thing I see printed should be Sammy. There it is. Okay, so I used, get rid of some of that junk on the screen, I used the dot operator before to access methods on a particular instance. I can also use, and more usefully, more importantly, I can also use the dot operator to access variables on a particular instance. Now if I print out x.firstname, what do you expect to see? What's the value of x? The value of x is the instance of a student with Lucas Lehmann inside as data. Now you can also take advantage of this dot operator to rewrite your data. So let's say Sammy is not satisfied being called Sammy. Let's say that she has updated her name and she's grown up and she's more mature. She wants to be called Samantha now. Okay, so why dot first name, the instance of a student holding Sammy Seahawk, dot first name, this is going to access the instance variable first name, and it's going to assign it a value, Samantha. So just like, you know, if you had a regular old variable, you could do an assignment statement that looks like this. Well, if you want to access a class instance variable, put the instance of the class first, which is y, dot, and then the variable you want. Okay, so now when I print, or let's do this, let's print y.firstname, the change will be there. It stays there. It's recorded, right? So Sammy's name went from Sammy on line 15. We reassigned it on line 16, and now we're, we have Samantha when we print it out, right? So that change was permanent. If we tell the student instance y to say its whole name by this, you will see that, you know, self.firstname has been changed. Okay, so this dot operator is very powerful, right? It lets you access both methods and instance variables on an object, okay? So again, really important slide. Digest, sit here and understand, digest what this dot operator does. If you're a visual person, the dot operator lets you take an instance x, y, and access the variables and the methods on that instance. That is the role of the dot operator. It's also the role of the dot operator in strings, in list, string.split, string.append, you are accessing a method on that particular instance of the class, okay? So big concepts, you know, really understand it. We'll have a big homework where we get some practice with it, and I'll show you an in-class exercise here in a second. Well, let me kind of start to wrap up here. Why go through all this trouble of classes? Why go through all this trouble of data types? The answer is to solve big problems, you need to be able to abstract away the details, right? So here's our Kia Sorento, kinda, my big head is over it, whoops, grab my head, move my head, right? When you're using a 2020 Kia Sorento LX front wheel drive, you don't care about internal combustion, you just want to drive, right? When you are using a Spotify playlist, you don't care how, what the mechanics are of adding songs and removing songs or playing songs, you just want to manage the playlist. When you're using Instagram, and you're scrolling through pictures, you deal in the concept of pictures, you don't deal in the concept of the individual pixel values or the compression used to store that data or anything else, it's a picture to you, okay? When you're picking classes in your course registration system, you don't think about the individual components of a course, you don't deal with instructor, time, location, course name, course number separately, you deal with them as a glob, all together, right? And that lets you think in terms of many different courses all in once, right? Instead of you having to manually connect the dots there, okay? So in life and in programming, it's incredibly useful to kind of abstract things together into types, into classes, so that then you can work on problems that are dealing with a type or a class, like in a course registration system. It's a little bit easier to think about it if you think of students and classes and instructors, rather than the individual data that make up the notion of a student or a class or an instructor, right? Classes and data types let us tame complexity, they hide the stuff, right? So we use classes, aka data types, to create concepts out of the constituent parts. We want the big picture item, not its dirty details. We need those dirty details for some things, but in general I want to deal with the concept of a student. I don't want to deal with 850 number, first name, last name, date of birth, phone number. I don't want to deal with those strings. I want to deal separately, right? I want to deal with a student and then that student kind of holds all that data inside and I can access it, okay? So we use classes to hide the details of how a thing works so that we can use that thing, that class, to solve bigger problems, okay? So I'll sum up here with a little bit of vocab. You've heard me use these words a lot. They really need to sink in now, okay? Because we're gonna be using these terms forever and ever in this class and in other classes in your future, right? Python classes are used to implement data types, okay? Classes define a new data type, allowing new instances of that type to be used in the code. Classes have instance variables for storing data and they have methods for operating on the data. Variables and methods, this is a new term, okay? Last one. Variables and methods together inside a class, those are called the attributes of a class. The instance variables and the methods inside a class are that class's attributes and you've used the dot operator to access attributes in a class, okay? So I'm gonna leave off here with an in-class exercise where I want you to go over to that the code we were working on with the student, add some more data in, also create another class. You can have multiple classes defined in a single Python script, okay? And I ask you to do some work here and check it out, run into it, try it, let me know what questions you have. Get comfortable implementing classes. You are going to be implementing classes from here till the end of the semester in this class, okay? So practice a little bit now. You'll have a big homework assignment to kind of get some practice reading data in from a file and then putting that data into a class instead of treating the data separately. All right, in the next video we're going to wrap up our discussion of classes by talking about more of those special funny-looking methods like underscore underscore in it. Next time.