Classes and objects
About this video
A class is a template for a custom data type, and objects (instances) are the actual things created from it, each storing its own attributes. Using a ShellGuy example, the video builds from a hand-written initialize function to Python's special __init__ method, which runs automatically when an object is created and receives the new object as its first parameter, self.
You'll be able to
- explain the difference between a class and an object (instance).
- write a class with an __init__ method that sets attributes on self.
- explain that Python calls __init__ automatically and passes the new object as self.
- create an object by passing arguments for every __init__ parameter except self.
Key words
- class
- A template for a data type that defines what kind of thing it is and what data it stores.
- object / instance
- An actual thing created from a class, with its own attribute values.
- attribute
- A variable stored on an object, accessed with dot syntax like guard.color.
- __init__
- The special initializer method Python runs automatically when an object is created.
- self
- By convention, the name of the first parameter, which holds the object being acted on.
Transcript
- We have a group of related data that models some real world concept like a weather forecast, a user account, or a game character. Each character is made up of multiple attributes like its color, its health, and whether or not its shell has spikes. We can certainly store all of those as separate variables, or we can use a data structure, like a list or dictionary to bundle those values together.
Great, but what if my program needs to represent tons of shell guys? We can make a separate dictionary record for each shell guy, but nothing in that code enforces that every shell guy has the same structure. All it takes is one little typo, somewhere in those hundreds of shell guys, and now our program is broken.
To solve this, we can create our own custom data type using classes. A class is a template for a data type. It defines what kind of thing it is and what data it can store. The class sets the rule that every shell guy must have a color, health, and has spikes attribute.
Each shell guy we create must follow that rule. But each shell guy is his own unique thing. So this shell guy might store green, eight, and false, whereas this shell guy over here stores blue for his color attribute, 50 for his health, and true for has spikes.
We call these actual shell guy things we create instances of the class shell guy, otherwise known as objects. Unfortunately, the syntax for this in Python is where it all gets a little weird. So let's break it down step by step. To define a new type, we use a class definition.
We put the keyword class, the name of our type, and then a colon. Like everything in Python, the rest of the definition needs to be indented inside this, which we'll talk more about in a minute. Okay, we defined a new shell guy type, but like a function definition, a class definition doesn't do anything on its own.
We need to call the class just like we call a function. Calling the class creates a new object of type shell guy, which right now is just an empty data container. We can then store data on that object using dots syntax. So this attaches a color variable to this specific shell guy.
We call these variables stored on an object attributes. So now this shell guy is keeping track of his data and I can create other shell guy objects and have them store their data. Now I'm seeing some repeated code here. After I create a shell guy object, I'm then immediately assigning these same three attributes.
How do we clean up repeated code? We use a function, so let's write a function initialize that takes in a shell guy object, we'll name that parameter self, and assigns that objects color, health, and has spikes attributes. That means instead of guard.color, we'll have self.color.
So after we create the first shell guy guard, we'll initialize him, and then the same for the shell guy object boss. But right now we're just hard coding the attributes to the same three values. That means every shell guy gets initialized to the color green. Instead, I wanna pass in values for those attributes so I can initialize my shell guys with different data.
So we'll add a parameter for each of these and we'll store its value in the corresponding attribute. Then we can easily initialize our guard shell guy to be green, have eight HP and no spikes. Whereas we can give our boss shell guy the color blue, 50 HP, and yes spikes.
This is all great, but I just wrote my own arbitrary function. Wasn't the whole point of a class supposed to be that it enforces objects of that type have the same attributes? Well, the people who created the Python programming language realized that programmers always want to create the object and then immediately initialize it, so they got a bit fancy with the syntax shortcuts here.
Python classes have a special initializer function, or technically we would say initializer method. Since the function definition goes inside the class definition, this method must be named, __init__. The magic is that python secretly automatically executes the initializer method when an object of that type gets created.
We can see that in action by adding a print statement here. This init method is executing twice. Once when we create this shell guy object, and once when we create this shell guy object. The other piece of auto magic is that python passes in the object as the first argument when it calls init.
Since we're in the middle of creating the object, we don't have a variable that stores it yet. There is no guard that we can pass in. So Python handles that plumbing for us and passes in the object it's currently initializing. Meaning that empty shell guy it just created.
By convention, we named this first parameter self, as in me, myself, the object we're acting on. The name self has no special power. We could name this potato. What's important is that this is inits first parameter. There's nothing truly magic here. We can call init manually after the object's created and pass in the object directly.
There's just no need to, since Python already called init automatically. With the auto magic outta the way, we can just move our earlier initialized function body into our special init method, adding those parameters to init as well. Here's the question, since we don't call init directly, how do we pass in those other arguments?
You might have guessed this one, but we just pass them in when we instantiate the object. When Python automatically calls init, it will automatically pass those arguments through in the same order, except it adds the object as the first argument. That means we as the programmer don't pass in an argument for self.
Init has four parameters, but we only pass in arguments for the last three. And one small cleanup, by convention, we name our parameters the same as the attributes that store them just to keep things simple. Okay, let's sum up what we did here. Our original initialized function and our special init method do the exact same thing.
They set up or store these three attributes on the object that was passed in. The special class magic is that Python automatically executes the init method when we create a new shell guy object, thus carrying out our desired rule that every object of type shell guy has the attributes, color, health, and has spikes.
Video © Khan Academy, used under CC BY-NC-SA. Watch on Khan Academy. Puzzicle is not affiliated with Khan Academy.