Hello, everybody, and welcome back. So in this video, you're going to learn about object oriented programming in Python, and how you can create your own custom objects by making different classes in Python. Now this video is geared towards beginners. So people that have some knowledge of Python have written Python code before, but have not yet stepped up into kind of the intermediate stage, which I'm going to call is this working with classes and objects and are looking to learn that so when we starting completely from the beginning, if you have no knowledge of object oriented programming, that's totally fine.
And this series is meant for you. you're someone who just wants a refresher, I'd recommend maybe watching the video and two times speed and slowing down when things get a bit more confusing, or when you know, you need to really learn that topic more. So let's go ahead and get started.
And what we need to do to discuss object oriented programming is first define what an object is. Now, a lot of people may think that when we talk about object oriented programming, that's a completely new thing. You know, you've never seen it in Python before. And it's a new kind of thing to tackle and to learn when in reality, you actually see objects all the time when you're working with Python code. And you just don't know that they're objects just because they're a little bit disguised.
And I'm going to kind of take away that disguise and show you what all these different objects are. So we may have heard of the notion of type before we see this function type. And we know when we ask it what type a certain variable is, or we ask it what type say, you know, a string is like this, it spits out an answer to us.
And I'll show you what I mean by that. So if I do print type of Hello, so we know that Hello is a string data type. Let's just see what we get in our console down here.
Oops, I didn't mean to do that. We get class string. So notice that this says class.
Now most people just disregard the fact that it says this, but this is actually very important. What this is telling us is this right here, this string that we typed in is actually an object of the class straight right str. Now, what do you think is going to happen when I put in x? Well, we know that x is an integer.
So let's have a look here. We can see that we get class int. So notice that these different data types that we've called them before a string and an int are actually a part of a class. And what that means is even though it doesn't look like we've created an object, when we do something like X equals one, we've said X is equal to the object, which is a type integer with the value one. That's exactly what we've done.
And the fact that we actually create these kinds of objects is very important in writing Python code. And I know this hopefully shouldn't confuse any of you too much. but if I write, say a function, so, you know, define hello. And in here we just print hello like that. And what I decide to do is I go, okay, let's look at the type of hello.
So notice I didn't call the function. Hello with the two brackets. I'm just looking at the actual name of hello.
And we run this well, if we add the final bracket at the end here, we get class function. So pretty much everything in Python that we work with is actually an object of some kind of class. And later on in the video, what we're going to do is make our own classes so that we have our own specific types.
So these are what are called the built in types. These are built into the Python language. And that's why they work a little bit differently than other classes that we'll work with later.
But just understand that whenever you create something in Python, you are really creating an object, which is an instance of a specific class. And that class defines the way in which that object can interact with other things in our program. And to show you what I mean by that, let's just look at some common error messages.
And we'll see exactly what that means. So we're gonna say x equals one y equals Hello, and watch what happens when I try to do something like print x plus y. Well, we get some error. Let's go up and see here we get type error unsupported operand types for plus int and string.
So it's saying because our object x is an int and our object y is a string, we cannot add them because the program does not know how to work with objects of those two types. Or in better words, the addition operation is not defined for objects of int and objects of string at being added together. So it's very important.
And pretty much the type that we have defines what we're allowed to do with a specific variable or with that specific object. And it denotes the actions that we can perform, you know, plus minus all of that, if I were to change this right to two, and then we just do x plus y, I don't need to print it out, we can see that that's totally fine. And there's no issue because these are both ints.
which means that operation is defined for them. Okay, so hopefully that is clear. And that makes some sense. I hope that gives you kind of understanding of what I mean by objects. And now I'm going to talk about methods, because those are something that we can perform on objects.
So I'm sure that many of you seen this before, I'm just going to type string equals, hello, like that. And many of you know that we can do something like print string, if I can type this correctly, dot up, right? So when I write this dot upper here, what is that? actually doing?
Well, let's look at here, we obviously know that puts that all to uppercase, but we're allowed to use this what we call method. And this is a method whenever you have this dot operator, you have some name, and then you have the two brackets at the end here. And maybe there's some arguments that go inside of there. that is usually a method that is acting on a specific object. And in this case, what it's doing is we have the method upper acting on the object of type string that is stored in this string variable.
And the reason we're allowed to use this method on it is because this is a string. Now notice that I can't do something like you know, x equals one, and then do x dot upper. And the reason I can't do that is because well, it's going to say right here, int object notice int object has no attribute upper So these methods, these different operations, the things that we can do with these objects is based on the type of class that they are.
So that's something that we really need to nail in there. And I hope that makes sense. And now what I'm going to show you is how we can actually create our own classes, because hopefully that gave you a good enough explanation for what objects really are. So to create our own class, I'm going to kind of do like a template here.
And then we'll go back through and discuss what everything's doing at once. But just try to follow along for now. So I'm going to say class, and we're just going to do the classic kind of animal example to start here.
So I'm going to say class dog, and we're going to define this initialization, which we'll talk about later. And actually, no, let's not even do that. Let's just say define bark like that. And then what I'm going to do in here is simply print.
Okay, so what I've actually done here, and I know this is gonna be weird for many of you that have not seen this before, is created a class called doc. What this means is I'm going to make my own kind of blueprint now. for any objects that are of type dog, and I'm going to start defining the operations that a dog is able to do. Now, in this case, I've created a method. Now a method is essentially just a function that goes inside of a class, that's the easiest way to define it.
And for our basic example, right now, all of our methods are going to start with a parameter called self. And we're going to talk about what this self parameter means and how all this works later on. But let's kind of build our way up to that. So I'm going to say D equals dog down here.
And what I've done when I did this here is I'm actually saying, okay, I'm going to have the variable D and I'm going to assign it to a instance of the class doc. So this is class dog. Again, you don't need to know how this works right now. We're going to talk about that in a second.
But when I write this line here and I put dog, which is the name of my class, and then two brackets like that, I am instantiating, right? Creating a new instance of the class doc. So D is now going to be an object that is of type doc and notice that we don't get any errors there. And when I decide to print out type of D, we're going to see that we get class underscore underscore main underscore underscore doc.
Now the reason we have these underscores is because this is telling us what module this class was defined in. Now by default, the module that you run is called the main module. That's why we have these two underscores main underscore underscore, but we can see that this is again an object of the class doc. And that means that whatever we define inside of here is going to be what is allowed or the operations that can be performed. by a dog.
And one of those operations is bark. So this is a method. Remember, we've talked about methods just like dot upper before.
So if I want to use that method on an instance of my dog class, because this is an instance of the class dog, then what I can do is I can type will not dot upper, but dot bark like that. So I can call this method on my dog object again, because it is a class dog. And I've defined a method that works for dogs.
So when I do that, we can see that we get bark, and then it's still So obviously continues to print the type of that object. So this is what we're getting at here. So these are kind of how classes work.
We name the class. Typically the convention is to use an uppercase letter. Um, and usually you go camel case.
So you do like dog, like you say, you're going to do two words. You'd do like dog. Hello.
Right? So camel case like that. And then inside of the method or inside of the method, inside of the class, you can define a few different methods and operations that can be performed by this object.
So what I'm going to do is I'm going to say define, I don't know, let's just say meow, I know this is wrong, because it's not a dog. And we'll talk about self in just a minute. But I can define another method here. And instead of printing something, I could say return, let's just say, meow like that.
So I don't necessarily need to print something, I can return something, I can have these methods take arguments. So maybe I have meow, and then I put x. And maybe what I do is I return x plus one or something like that.
So maybe let's just call this ad, call this ad underscore one. that'll be the name of our method. So I can return x plus one. And then what I could do if I wanted here is I could say dog, add underscore one, put five there.
And let's print out the value that we get. So let's have a look at this here. And we can see that we get the value six.
So we can make methods that have different arguments associated with them or parameters like I put x here. And that just means that when I call that method, I need to pass in a specific value or specific values so that that can operate and that can work. So that is how the methods work. So now we're going to talk about self. But before we can do that, we need to talk about what's called the init method, which you may have seen me typing at the beginning before I kind of gave up on it, because I didn't want to get that complex that fast.
So right now we have two methods, we have this bark method, and we have this add one method. And I hope those make sense on how those work. And now we're going to talk about this special method here. And by the way, this has two underscores.
So it has underscore underscore, and then a knit, and then underscore underscore. Now this is a special method. it. And what this pretty much allows us to do is instantiate the object right when it is created.
So this method will be called whenever we write this line. So whenever we create a new dog instance by writing dog, and then the two brackets, let me get rid of this down here for right now, this method will be called, and it will pass any arguments that we put in here. So maybe I put something like Tim, in here, we'll pass that to this method.
So let's say that every time we create a dog, and we want to give it a name immediately. So to create a dog, the criteria is that you must give it a name. Well, what we would do in here is we would put the parameter name, and then here we would pass in the name. So what we need to do though, is what we're passing in this name, we need to store this in the dog object somewhere, right, we need to have this stored, we need to be able to access this, this is kind of the nice thing about objects.
And this is where we're going to talk about attributes. What we can do if we want to store this name is we can say self dot name. equals name.
Now it's important to note that this doesn't these don't need to match. But what we've just done here is we've created what's called an attribute of the class dog, which is named. So what this means is essentially every time we create a new dog object, we will pass a name through this parameter. So self just stays here to denote the object itself.
So like, every time one of these things is called, we will pass a reference to which object it was called on, so that we can access things for each specific object. And we'll talk about that more in a second. But here, what I've done is define an attribute called name, which is equal to the name that we passed in. So what I'll do is immediately, I'll just print inside of here, the name so that we can see how this works. And notice that when I run this, now we get Tim printing out.
So even though I didn't explicitly call this a knit method, because I wrote this line here, it passed this name that I put in here to the name parameter, and then it printed out name here inside of the unit. And we also defined an attribute called name that is inside of this dog class. So what self is doing is every time that any of these methods is called kind of invisibly, you don't get to see it, the actual reference to this dog object is passed so that we can access attributes that are specific to each dog.
And what I mean by that is I can make another dog. So maybe I say D two, which is, you know, another instance of the class dog, except this time, I name it Bill, right? And now we'll do the print statement again.
So we'll say print name just to show how this works. In here, we get Tim and we get Bill. So we actually have two different dog objects now, one named Tim, and one named Bill, and they both store different names inside of their, you know, instance, if that's what you want to call it.
So let's just show how this attribute works. So the point of this attribute is that it's stored permanently for each specific object. So I can actually go ahead and go down here and print.
D dot name after that's defined, and I can print D two dot name like that. And when we look here, we can see that those names get printed out. So Tim and Bill and notice that I removed the print statement from up here.
So these attributes when we do something like a self dot, and then anything that we want, we can call it whatever we want is equal to some value, we can reference them later on. And we can reference them from methods inside of our class. So an example of that is something like, say, define, get underscore name. And So the first argument here is always going to be self, or the first parameter. And the reason for that is because we need to invisibly pass the actual, you know, dog object itself, so that we know which dog we're accessing, when we're going to get say the name of the dog.
So here, we're going to say define get name. And all we're going to do is return self dot name like that. So now rather than saying dot name, we can call dot get underscore name, which is a method, right.
And we can do that down here as well. Get dot get underscore name. And then we see that we get Tim, and we get Bill. So that is the basics behind objects. Now, of course, I can make more attributes as well.
So I could do something like age. So let's say my dogs, I want them to have a name. So let's say my dogs want them to have a name and an age.
Well, what I'll do now is I'll say, Okay, so in our initialization for dog, we're going to take a name, and we're going to take an age. So we'll define the attributes in here. So self dot age is equal to whatever age we passed in. And now every time that I make a new dog object, I need to pass a name and an age.
So these this Tim will be an old dog here, Bill will be a young one. And we'll run this. And we see that we get no issue.
And if I wanted to, I can go ahead and define another method here. So get age, and we'll return self dot age like that. And then if we change this to get age, and we change this one down here to get age as well.
What is the issue here? Oops, so I'm forgot to put myself in here. So notice what happened when I forgot to put myself. So that's actually a decent error to run into. I didn't put self in here as a parameter.
And you can see I know that's kind of messy here. It says get age takes zero positional arguments, but one were given. So what does that actually mean? So it's saying it takes your arguments, which it did when I didn't have self here, but one was given, but I didn't give any arguments inside of the brackets. Well, that's because when we call a method, this right, the actual dog object itself, immediately kind of invisibly gets passed to that method as the self parameter.
So we know which dog it's talking about. So now if I go ahead and I add self, we can see that this works fine, and we get 34 and we get 12. So that's an important thing to remember. Now what we can actually do is we can create other methods that modify these attributes or create new attributes. So I can do something like for example, set age. So I can say define set age will give itself of course, and then we'll put an age here.
What I can do now is say self dot age is equal to age. And now what I'm going to do for Tim, and we'll get rid of bill for now, because I think we get the point on how it works for different objects is going to say D dot set underscore age, I'm going to change his age to 23. And when we print it out, we can see that 23 actually gets printed out. So we can modify and we can access these different attributes from methods inside our class. And this is where things get things get very powerful, because this allows us to access data that is stored within a specific object and do different things with it.
based on how different methods and different things are being called, right. And this just is pretty much the blueprint that defines how a dog actually works, how it operates, what it can do the methods associated with it, and the attributes that exist. Now, some of you may be saying, Well, why do I need to do this, right?
Like this seems kind of redundant, I could write in a different style. Well, the nice thing about object oriented programming is once you create one of these classes, you can have an infinite amount of instances of this class. without having to change anything.
So let's say for example, that we'll leave that class up here right now. But we want to simulate what we've just done here. For the Tim dog, right, we want to have an age and we want to have a name and we want to be able to change his age and all that.
Well, many of you that were more beginner programmers would probably tell me we can do something like this, we can say dog one, underscore name equals Tim, you could say dog one, underscore age equals, you know, 34. And there you go, you just define these two attributes. If you want to get it, you can just access the variable. If you want to change it, you can just change the variable. Okay, nice.
But what happens when I want to make 25,000 dogs, or every time I run my code, I want to make a different amount of dogs, you can't find a way that you can write all these different variables that have 123 all the way up to 50,000, or however many dogs I have to represent that. That's why we use objects is whenever we're going to be kind of reusing something, there is some instances in which we make a class where that we're only going to use once or instantiate once. But those are kind of more complex examples that we won't get into here. But then okay, so some of you might tell me, Alright, well, Tim, I can just do list, I can say dogs equals that, and say dogs, underscore name equals that, right.
And here we can put, well, the first name is Tim, and then we had Bill. So this will handle the idea that we can't just have all these different variables, right? So dogs age equals And then we go, let's say, I don't know, 3214. Okay, that's great.
But the issue with this is that it's really a pain when I want to access the dog's age, the dog's name. And then what if I had like 25 other attributes or methods associated with the dog, this is just just a pain, you don't want to deal with that. Because I have to now find the index of whatever dog I want in one list, which is a very time consuming operation and computing. And then I need to reference that index and all the other lists for all the other attributes.
And it just becomes very messy, very quickly. And let's say I want to delete one instance like the, you know, the dog object bill or something like that, right, then I need to find the index of this, I need to find the index of all of the attributes and all the other lists, and I need to delete them at the same time, to make sure that all my data stays consistent. And there's no kind of offsetting things are too many attributes in one list. So it just is a pain to do it like that.
So I hope this makes sense on why we would do object oriented style. And now what we'll do is go into one example where we create a more complex object and show kind of the advantage of that. Okay, so a lot of people typically will stop at kind of the previous example that I just gave you.
Now I want to go a little bit further and show you the advantage of doing multiple classes. So rather than just using one class, I want to show you how different classes can kind of interact. In an example where I have a bunch of students, these students all have some grade assigned to them, and they're all a part of a course.
And then that course will have some methods on it to do things like find the maximum grade of all students give us the average grade of all students tell us the lowest grade. some things that you may actually want to do, say, if you were trying to model or create a system for you know, a school or something like that, right. So this is obviously going to be a little bit more of an example as opposed to super practical, but hopefully this should give you some more insight into how we would do something like that. So I'm going to start by cleaning, creating a class called student, I'm going to go a little bit faster here, but I will slow down and talk about what I've done after.
So don't worry if you can't keep up. So we're going to have a student and each student is going to have a name, age and a word. So we're just going to say self dot name, equals name. self dot age equals age, and then self dot grade equals grade. Now this numeric grade is going to be between zero and 100. So I'll just write that down here just so we know that.
And that should be good for our students. So we could of course, go in here and add some methods if we wanted something like get underscore grade. And we'll just add one just so we have it in here. And what we can do is return self dot grade like that. Okay, so this is this is where we're going to leave our student now.
we could add a lot more things to this, we could add a change grade, we could, you know, add a test, we can have weightings, we can do all kinds of crazy stuff. But for now, we're going to leave it at that. So again, we've defined three attributes here, we have a name and age and a grade, those are equal to the name, age and grade that we pass in when we initially create a student.
And we have one method, remember, this is a method that simply returns a student's grade. Now I'm going to make a new class called course. Now what I'm going to do in this course class is I'm going to have the ability to add students to a course.
So when we create a new course, what we're going to need to do is we need to define the name of the course, and the max underscore students that can be enrolled. So in here, we'll say self dot name equals name, and self dot max underscore students equals max underscore students like that. Alright, so what we're gonna do now is we're going to add a method that will allow us to actually add students to this course.
But how are we going to do that? How are we going to have students stored inside of a course object? Well, what we can actually do is we can make a list of students. So I'm going to say self dot students equals a blank list.
Now notice that I've made an attribute and I didn't assign it directly to one of these things that was passed into one of these parameters, arguments, wherever you'd like to call them. That is totally fine. In fact, a lot of times we'll create attributes like self dot, you know, is active, something like that equals false.
right, we can do that, that's totally fine. And attributes are just whatever we decide to define. And if we want to assign them to say the argument or parameter that's here, that's fine. And we can do that.
So there we go, we have self dot students, which is a blank list. And I'm going to start by creating a method here that's going to allow us to add a student object into this list. So I'm going to say define, add underscore student like that.
And in here, we're gonna take self and we're actually going to take student Now this student right here is actually going to be an instance of a student object. And I'll show you what I mean by that in a second. But all we're going to do is say, if the length of self dot students is less than, and in this case, self dot max underscore students, then what we'll do is we'll say, self dot students, dot append student.
So we're going to create a list of students inside of this, you know, them, this list right here, right, that's what we're gonna do. And we're gonna add them in only if this is less than the maximum students in the class. And what I'll do is I'll actually return true if the student was added successfully.
And then down here, I'll return false, if not, so that we can tell maybe if we make a program down below, if that student was added properly or not. Now I'm going to add another method. And here I'm going to say define get average grade. Now we'll code that one out in a second.
but let's do an example of how we can actually now add students to these classes. I'll make that a bit smaller so we can read it. So let's make a few different students.
So let's say S one equals students. What do we need to pass for student and name, age and grade. So let's call that Tim.
Let's call that 19 and let's call that 95. Tim's a smart guy. All right. And then we'll do S two equals students. We're going to say bill, we'll put him at 19 as well.
And he's not as smart. He is 75. And then let's do S3 equals and let's go Jill. Keep with the trend, Tim, Bill, Jill 19. And maybe she has a 65. Okay. So we've created three students here.
Um, and these students are proper. They should work fine. Let's run this code and make sure everything's all right.
Now, how can we actually add them to our course? Well, the first thing we need to do is make our course. So we're going to say course equals course like that.
What do we need when we make a course we need a name and we need a maximum amount of students. So let's name this course. let's say science or something like that.
And let's say the maximum amount of students is actually going to be two, because I want to show what happens when we add a student past the maximum. Now we're going to add students. So how do we add them? Well, we have to call that method. So of course dot add students.
So there we go, we can add student s one. And then let's do course dot add student again, and let's add s two. So now let's run this, we see everything works fine.
And let's actually make a thing that can you know, print out some thing about our student or can show our students. So what I could do is I could say, let's print down here course dot and this students. And let's have a look here and we get main dot student object at some gibberish location, main student object at some gibberish location.
So what this is actually telling us is both of these things inside our list right now are student objects. And notice, what I can actually do is let's say index the zero with item in that list. So the first student that we added, and I decided to call dot name on it, well, then that should hopefully print out the name of that first student, which is 10. So that's cool.
And that's how we do that, right? We can add things into this course. So now this course stores all of our students.
And since all these students have a grade on them, inside of our course, we'll be able to access that. So now that we've done that, let's write this method that can get the average grade of all of the students that are enrolled in the course. To do that, what we're going to need to do is grab all of the students from the students list, we're going to need to add that to an average, and then we're going to need to divide that value to figure out what that is. So what we'll do is we'll say, value equals zero, we're going to say for student in in this case, self dot students, then we're going to say value plus equals student dot gets grade like that.
Now notice that we could just type grade if we wanted to, but I usually just like to use the method because say we were to change any attribute later, then this code wouldn't break so long as this method was still named the same thing. And we can modify the method in here. So they return the proper grade, like say we had a student enrolled in multiple courses, then we could, you know, determine their grade in a different way. And when we call get grade, maybe we don't just return self dot grade, we return something different. So this still continues to work.
So we'll say value plus equals student dot get grade. And now all we'll simply do is return return in this case, value divided by the land of self dot students. So let's see what the average grade of our courses so course dot get average grade, we'll actually print that out, like that, and run that and we can see that the average grade is actually 85. And that makes sense with the grades that we have here. So that's the idea behind what I'm trying to show you is that we have, you know, we can have different classes, we can attributes with them. And when we can program an object oriented style, now it doesn't matter how many students we have, or how many different courses we have and what students are enrolled in which in fact, what we could do is we could make another course, we could add the same students to it.
And then we would obviously have to change the way that their grade was calculated. But that's something that we could do. Now let's just look at what happens when I decide to try to add that third student. So of course dot add student like that, and we decide to add student s three, we can see that we get the value false. And notice that the average grade does not change, because we didn't actually add that into the course, right?
Okay, so we finished off the basics on classes and objects and how to create our own classes. And hopefully that last example helped you to really kind of understand the advantage of doing this. Now what we're going to talk about here is something called inheritance.
Now, this is where we slowly start to get into a little bit more complex and some more difficult concepts. So try to follow along. But I don't find inheritance is that extremely difficult. So the idea behind inheritance, and we'll show what that is in a second is you have two classes that are very similar. So let's say we have a general class called maybe our actually, let's do a better example.
Let's say we have a class called dog and a class called cat. And in fact, let's actually code these out so that we can see this for real. So let's say in the init method of the cat, what we're going to do is we're going to have self name and age like that.
So we're going to say, you know, self dot name equals equals. if I can type properly, which apparently is not happening right now, and then self dot age, and then we'll do define speak. And all we'll do in here is just print, you know, what is this cat? Yeah. Okay.
And then let's say we have a dog. So we'll say class, dog like that. And we'll define underscore underscore net underscore underscore, put the self put the name put the age because that's all we want for that.
And we'll say self dot name equals name. self dot age equals age, and then define here speak. And the only difference between these two classes is actually going to be the fact that this prints bark instead of meow. So notice that these two classes are almost identical. In fact, there's only one line of code that's different other than the class definition at the top.
So there must be a way that we don't need to write this twice that we can actually use what's called inheritance so that these dog and these cat classes can well inherit from an upper level class, which means that all that functionality is defined in one place. And we only need to write what's different about those two classes inside of them. So ideally, what I would like to have is to be able to remove this init class from both of these and just have the speak method because the only thing that's specific to a cat and a dog, at least for my example, is the fact that one of them says meow, and one of them says bark. So let's show how we can actually do that. So by removing that, um, you know, was a knit method and, uh, just having these methods here, what we need to do is make an upper level class, which I'm actually going to call pet.
So I'm going to say class pet. What I'm going to do in here is define that a knit method that we had before. I'm actually going to define a, um, let's say show. So this show method is just going to show me all the things about my object.
So I'm going to say print. And in here, we're going to say, you know, I'm actually going to use an F string, you may not know what that is, but don't worry about it. I am self dot name, and I am self dot age, years old. Okay, so what I've done is I've defined this pet class. And this pet class essentially is going to contain the functionality that I want the cat class and the dog class both to have.
And then inside of the cat class, and inside of the dog class, what I'll do is I'll define the methods or the attributes or whatever it is that I need to do that are going to be different for this specific class. So notice that pet is general, we call this a generalization, whereas cat and dog are more specific. So how do I actually allow the cat class and the dog class to use this functionality?
Well, what I can do is simply add brackets and write pet. Now what this stands for is I am inheriting the upper level class pet. So we're saying this is the general class. This is a more specific class that's being created and inherits from pet.
And same thing with dog. Let me show you how this works. So let's create an instance first of all of the pet class.
And then we'll make one of the cat and the dog class and I'll show you how it works. So let's say p equals pet. Notice that for pet, we need a name and we need an age. So let's say Tim, let's put them at 19. And then let's do p dot show.
So let's look at this here. I am Tim and I'm 19 years old. So that is how the pet class works pretty straightforward.
Now, let's make a cat class. So let's say C, we're gonna say C equals cat, we're gonna say the cat's name will be Bill. And that cat will be 34 like that. And then let's do the same thing here, see that show.
Now notice that even though there's no method called show inside of cat, it still pops up and says I am Bill and I'm 34 years old. That is because it inherits inherits. the properties from the pet class, because I've defined that up here.
And notice that even though I didn't define an init method in here, this still worked fine, right? There's, you know, this was okay, we initialize because we use the init method that was defined inside of pet. And of course, we can do the same thing with dog.
So we can say D equals dog, what do we call this one before Jill 25. And let's do D dot show like that we go I am jail and I'm 25 years old. So that works. but now let's show what happens when we call speak on the cat and the dog. So if I decide to call speak like that, and we'll call it on both of them here, we can see that we get meow and we get bark. And again, that's because the speak method is different for the cat class and different for the dog class.
And since it's defined inside of here, and we created an instance of cat, when we make a cat instance, well, we're going to use the speak methods that's defined here. And in fact, what I could actually do is defined speak up here. And I could say define speak and I could say print.
I don't know what I say like that. Okay. And then if I decide to change this from show to speak, notice that we're calling speak three times speak is defined here and it's defined in both of our child classes is what we call them.
So when they when this is the upper level class, the more general version, any classes that inherit from it are known as the child classes or the derived classes. That's not that important to know, but just, you know, figured out through that lingo out there. And notice that when I run this, we get I don't know what to say meow and bark. So if there is a method defined in the lower level class, or the child class, that is the same name as the upper level class, it will automatically override that method. So it will take over anything that's defined in here is more specific to this class.
So it will use that rather than using this upper one, right? And you might ask, well, why would we even bother defining one in here if it were just going to take it over later? Well, we might create a another pet, right? Maybe like a fish or something, for example, like I can do something like this class, fish, pet, like that, I can literally just define that and put pass inside of here. And now what I can do is say, Okay, let's say f equals fish, and then we need a name.
So let's call this bubbles, why not give it 10. And now we can say f dot speak. And notice that we get I don't know what to say for this fish class, because there was no speak defined inside of here, it used to speak that was defined in the upper level or parent class, right, the one that we inherited from. So that is the basis behind inheritance. Now it gets a little bit more complicated, I'm going to show you the more complex aspects, because let's say I want to add one attribute to my cat. So let's say that for cats, we actually care what color those cats are as well.
Well, what I would need to do to do that is change this initialization method, right? Because I want to pass that in when I create a cat, but I don't necessarily want to rewrite this whole thing. So what I'm going to do And I'm kind of need to rewrite the whole thing.
But you'll see why we would actually do this in a second. I'm going to say self name, age, color like that. And now all I'm going to do, let me say self dot color, equals color. So what some of you may say here, now that we've defined the color in here is that what we should do is take this name and age, right, just copy it and paste it in here.
Now, that would be a correct answer. But I'm going to tell you why we shouldn't do that. So the idea here is that sometimes times in the initialization method of our parent, other things are happening other than just redefining attributes, right.
And in that instance, it would not be correct for us to simply omit the fact that we're not going to call this a net from the parent, we're just going to define the attributes, because that could mean that we miss out on a very important, you know, function that's happening from inside of this initialization. To give you an example, like say, in some web applications, maybe this initialization actually calls a database, right? and it asks for some information for a database and it sets up the object using that it wouldn't necessarily be correct then for me to just, you know, take the attributes that were in here and just redefine them as attributes here, I would actually still need to call that initialization to make sure the object was set up properly.
So to ensure that that happens, when we do an inheritance like this, we do need to define the arguments that we need are the parameters that we need for the parents initialization. So name age, but there's a fancy way to call that. So rather than you know, rewriting this here, I can actually explicitly call this and set up our object that way. So to do that, I'm going to say self or super dot underscore underscore net dot underscore underscore, name age.
Now what this is saying is super stands for reference the superclass and the superclass is actually the pet class or the class that we inherit from here. So that's what super stands for the class that we have inherited from then underscore underscore net underscore underscore defines the method that we want to call, right, and then name and age are the arguments that we're going to pass to that. So name and age, notice, I don't need to pass self, that's fine, we don't need self. And it's going to call that.
And what's going to happen is it's going to run whatever's inside this initialization, then that's going to set up the name and the age for our object. So we will have those properties defined. And then we will call self dot color equals color, we will execute that line.
So now we have the color, right? And if I just go ahead and want to define here show. So I want to change the show method baby for this cat object.
And what I can do is I say I am self dot name and I'm self dot age. And I am and in this case, self dot color, right? So we can change the show method in here.
And now let's go to cat. Let's change this to show let's add a color because we need to add a color. Now we've defined that in the net.
And let's run this and see what the issue is now that we're getting fish is not defined. Oh, did I I guess I've deleted fish or we got rid of it at some point. I did not remember. Okay. So anyways, let's run this and we can see that we have, I don't know what to say.
I'm bill and I'm 34 years old and I'm Brown and then we get bark, right? So that is how that works for inheritance. And this is how we call the superclass or the upper level parent class, right?
We need to call this initialization method before we just go ahead and do anything else because that parents initialization may be important. It may do other things. It may call another method, right? So we can't just simply skip that we need to call that explicitly by writing this line.
Now this line is kind of complicated syntax, you know, it's easy to forget, but try to remember it super again, references the parent class pet, and then we have a net and like that. So I hope that gives you an idea on how inheritance works. Now, it's hard to give really good inheritance examples without going more complex and more into detail.
So I'm going to admit doing that for now. But just remember that when you have classes that do a very similar thing, they have almost everything identical, except maybe a few attributes or a few methods, it might be a good idea to what we call generalize and make a parent class that is a general class that defines functionality that will be used in all of your child classes. And that is a very common practice and object oriented programming to use inheritance.
For example, a very good example is, for example, a good example is something like a person hierarchy. So let's say you have in you're working in an organization. And the example we want to consider is, we have managers and we have employees. Now managers have different access than employees.
But employees and managers have very similar properties, they'll have a name, they'll have an age, they'll have an ID, they have a birth date, they have many different things that they are the same for both of them. Well, if we were trying to model that system, and we were going to program that and make that what we would likely do is create an upper level general class called person. that defines all of the attributes and all of the methods that are general to all people, whether they are managers or employees.
And then we would create two child classes, one for employee and one for manager. And that would define the specific things that the manager can do and that the employee can do that are different from each other. So that's the idea behind inheritance. Hopefully that makes sense. And now let's move on to our next topic.
Okay, so now it's time to talk about static and class methods and class attributes. And in fact, we're actually going to start with class attributes. Now previously, you would have seen that every time we defined an attribute for one of our objects, we use self right and inside the class, we had self everywhere, self was referring to the instance in which we were talking about in that, you know, context, right.
So here, what we're going to do now is talk about class attributes. Now class attributes are attributes that are specific to the class, not to an instance or an object of that class. So I'm going to do a basic example, where I just create a class person. And I'm going to say, number of people equals zero. Now in here, I'm going to define a net method and say define a net like that.
And we're just gonna say each person will simply have a name, keep it nice and simple, like that. So self done name equals name, let's make p one person, we'll talk about what I've done in a second here in case anyone's confused. And let's say p two equals person. Let's make this gel. Okay, so we have this number of people thing.
And I'm sure a lot of you are like, what the heck is that? Well, this is a class attribute. And the reason it's not a regular attribute is because it does not use self. So because it's not defined inside any method, because it does not have access to an instance of the class, it is defined for the entire class, which means that this is not specific again, to any instance, it's not going to change with from person to person, whereas we know something like self dot name will be different for each instance of the person class. This is not different for each instance of the person class.
In fact, it's the same. So what I can show you is that I can actually go ahead and print say, you know, p1 dot number of people. And that gives me the value zero. But what I can also do, because this is not specific to the instance of any class is right person dot number of people.
And the reason I can write person is because again, this is specific to the class, not to the instance. So we can access it by just using the name of the class. And that actually means that I can change it using the name of the class as well.
So person dot number of people equals eight, right? And then if I go ahead and say, Okay, p two dot number of people, notice that we get eight, even though I didn't explicitly change it on p two, it changed for p two, because this was specific to the class. And when I reference this, all this says is, when I say p two dot number of people, the way that Python interprets this is what is the type of p two?
Okay, that's person notice, you can see it's popping up here, it says person, then let's say it? Does it have an attribute called number of people? No, this person itself does not.
Does the class have an attribute called number of people? Yes, it does. Let's display that. And since we changed that to eight, that's why we're getting that value. And of course, if we do this for p one, like we've shown, right, we'll get the same number, we'll get p one dot number of people get eight.
And if we decide to change this halfway through, right, so we do person equals nine, obviously, that will become nine now just because we changed it right before we printed the next value. So that is the basics. behind class attributes. There's a lot of different uses for them. Now, in this case, what I want to do is have a number of people.
So what I was going to do is inside of here, say person dot number of people, plus equals one, so that we keep track of how many people or how many instances we have created of this class person. So now if I decide to print P, you know, person dot number of people, and we'll do that after we create the second person as well, see, we get one to and this automatically increments. So that's a basic example of when you would use a class attribute, they're not extremely useful, but you know, is something that you may want to consider. And sometimes say you want to define a constant, something like maybe gravity, or something that's going to apply to every single person that you want to be a constant value, then you define that as a class attribute. So that if you ever decide to take this class and use it somewhere else, that constant is still defined as opposed to putting it up here, like as opposed to saying gravity up here and making it equal to you know, negative 9.8 meters per second at the top, what you would do is you would make that a constant inside as a class attribute.
So that now every time you want to access the gravity property for you know, a person, what you would do is you can reference directly the person's class constant of gravity, rather than a global constant, which may not be there if you put this class in a different file. And that's the idea behind this, these classes as well as that they are exportable. I can write a class in one file, and I can take it and move it to another file. And hopefully, it should continue to work, assuming it does not depend on anything from the previous file.
So ideally, you want to make your classes as robust as possible, which means that they don't need anything outside of their initial class definition, unless that's going to be another class that maybe it's interacting with, like in the example before we had course and we had student, but that is the idea behind class attributes. Now let's talk about class methods. So class methods, are defined a little bit differently than regular methods. And in fact, I'll show you how they work, we have a good example kind of setup here. So I'm gonna say define, and in this case, we're gonna say, number of people like that, instead of saying, actually self, we're gonna say CLS.
And what we're going to do here is return CLS dot number of people. And we're going to use what's called a decorator to denote that this specific method is a class method. And to do that, we write at class method directly above the function, or both, yeah, I guess function method, whatever you want to call.
So I know this seems a little bit weird. But the idea behind this is this method here. is not going to be acting on behalf of one instance, it's not going to be specific to an instance.
And in fact, you can call it on instance if you want, but that's not really going to be very effective. What this is meant to do is be called on the class itself, so that it can deal with something like, you know, returning the number of people that are associated with this class. So these are class methods, they that means they act on the class itself, they do not have access to any instance. And that's why I've written CLS here.
instead of self, because there's no object, what it's doing is just acting on this class. So for example, so we wanted to add to the number of people, then what I could say is class method, define add person like that CLS, and we'd say CLS dot, what is it number of people like that plus equals one. So that would be these are class methods, we denote them with at class method, just so we know that they're not referencing, you know, the know, self like that, they're referencing the class. So let's actually have a look at how we use that now.
So we don't need to necessarily print the number of people anymore. What we can do is we can go down here, we can say person dot number of people. And I don't know why there's so many brackets showing up there, geez.
But if we look at this, what we would need to print it out first, we should hopefully get the value of two. So let's look. Okay.
So we need to rename this so that this is not the same as the attribute, because that's going to be confusing. So let's add an underscore there. Cause I was realizing what the heck was going on there and we can see that we get zero. Oh, so that is because I did not continue to add here.
So actually what I'm going to do is, and this actually be a good example. So let's illustrate this here is I wanted to say too, but I forgot that I forgot, you know, didn't continue adding this, but what I can do is actually use the class method that I've defined here to add a person. So I can say person dot add person like that inside of my init.
And what that will do is call the class method on the class person, and then it will add to the number of people. So now when we run that we get to so that's how a class method works. I don't need access to the instance to call it although I can use an instance to call it if I want, I can simply reference the actual name of the class.
And this does not access any specific instance, it only accesses these class attributes, or anything specific to the class itself. Okay, so that is big class methods. Now, let's get rid of those.
And let's talk about static methods. So sometimes I'm actually gonna delete this entire thing, you want to create classes that kind of organize functions together. So for example, you know, when you say like, import math, like that, and then you get access to all these math functions, like math dot abs, or math dot square roots, or whatever it is you're going to use, well, what they sometimes end up doing in an object oriented programming, this is pretty common, is when you have a bunch of functions that you would normally just define, like you define, like, add one like this, I would do something you define, add to like this, well, what you want to do is you want to actually organize them into a class.
And the reason you do that is just so it stays a little bit structured, you can move all those classes together to another module and continue to use them. And to do something like this, you want to use what's called a static method. Now let's make a class called math. And what I'm going to do in here is I'm going to define some methods or some functions that I'd like to be able to use, but that are not specific to an instance.
So I don't want to have to make an instance of this math class to be able to use these methods, I want to be able to call them at any point. And it doesn't matter if I have an instance of the math, math class or not, I would like to be able to use them. So what I'm going to do is actually create what's called a static method.
Now static means not changing, right? It means staying the same. And that is a really good way to describe what these methods do, because they do not have access to an instance, just like the class method. All they do is something they do something but they don't change anything.
And that's the idea behind a static method, they don't change anything because they can't, they don't have access to anything. So in here, what we're going to do is just say define add five. And in here, I don't even need to put a cell for a CLS. Because this is not going to access anything, all it's going to do is just act as a function that is defined inside of this class.
And again, some of you I'm sure are yelling saying, What's the point of doing that? Why don't I just find a function globally? Well, it's more of an organizational thing. And there's some more specific applications in which you would use a static method.
So I do need to show them to you. So here, we're going to take a number and all we're going to do in this method is return x plus five. So now if I want to actually use this, what I can do is I don't need to say like, M equals math like that and make an instance, that's not necessary, I can simply write the name of the class and say math dot add five, let's put five in here. Let's have a look. If I got rid of the s and we get the value 10. So this is called a static method.
And we can make as many of these as we would like, just as we can make as many class methods as we want. So maybe we do add 10. Right, and then we have that like that. And we can change this method to say add 10. And now if we run this, we get 15. So that is a static method. Notice, it doesn't need anything. In fact, what I can actually do is say define, you know, PR, let's say that's going to stand for print.
Let's make this an app static method. And now let's just print run, like I'm just showing that you that you don't need any attributes in there are arguments, and let's just call PR. And now you can see that we get run.
And since I printed the value of that, it printed out none, but there we go. Like that. Okay, so that is static methods and class methods. And to be honest with that, that is pretty much everything you need to know about classes and objects, at least at a beginner level.
Now there's some more interesting things that we could talk about. But in the idea of keeping this more for beginners and so that everyone can kind of understand doesn't get too confused. I'm going to refrain from discussing anything further, but I hope this really gave you a fundamental knowledge of how classes and objects work in Python.
A little recap here is to remember that everything we work with is an object in some sense, we have functions, which are objects, we have strings, which are objects, integers are objects. And what an object does is it's an instance of some class and that class defines the properties and almost kind of the blueprint. for that object. It says, okay, so if we have a string, we can use the method like dot upper dot lower. If we have an int, we can add integers together and a class.
The type of an object is very important because it defines the behavior in which it can exhibit. So that has been classes and objects in Python and introduction to object oriented programming. I hope you guys enjoyed.
If you did, please make sure you leave a like these videos are not that easy to make and they are definitely time consuming. So I would appreciate it. Subscribe to the channel.
And of course, course, let me know if you have any questions or if there's anything you would like to see in the future.