Tracing objects

Learn this with friends

ComputingIntro to computer science - Python7:538 check-ins

About this video

The video traces a Triangle program in memory: a class definition teaches Python a new type, creating an object allocates an empty container that points to its class, and __init__ runs in a temporary stack frame with self pointing to the original object. It then traces attribute lookup, method lookup through the class, get_area, and the mutator method scale, showing that changes to self persist after the method ends.

You'll be able to

  • describe what Python does when it creates an object and runs __init__.
  • explain how Python finds an attribute or method using dot syntax.
  • trace a method call, including its stack frame and the self parameter.
  • explain why changes made through self remain after a method returns.

Key words

stack frame
Temporary memory created for a function or method call to hold its local variables and parameters.
reference / pointer
A link to where something is stored in memory, like an object's link to its class.
mutable
Able to be changed; user-defined objects are mutable by default.
mutator method
A method that changes an object's attributes, like scale.
pass by assignment
Parameters point to the original objects passed in rather than copies.
Transcript

- [Instructor] How does the computer represent an object in memory? And what happens when we call a method? Our program starts with a class definition that defines a triangle type. Similar to a function definition, when the computer encounters a class definition, it doesn't directly execute all of this code.

Instead, this class definition teaches Python about this new type, triangle. Python learns that a triangle has the methods init, get_area and scale. There are some nuances to exactly how this gets stored, but let's think of it as Python memorizing all of this code here and storing it under the name, triangle.

Okay, let's scroll down to our next line of code indented outside of the class definition. Here we have an assignment statement. So like always, we evaluate the right hand side first. This calls something named triangle, so Python checks its memory to see if it knows that name.

And under the name triangle, Python finds a class. So Python goes into object creation mode. Its first step is to allocate space in memory for that new object. It doesn't store anything here yet. It just makes an empty container that it declares is of type triangle.

So this object remembers that. Python stores a reference or a pointer to where that triangle class is stored in memory. Now that the object is created, Python automatically goes to initialize it. So it calls the init method on that empty triangle object. A method is just a special kind of function, so like a function call, when we call a method, Python creates a new temporary stack frame to store any local variables.

That includes our parameters, self, base and height. Now, if we've learned anything about methods, it's that Python automatically passes in the object for that first parameter, self. But if you recall, Python passes arguments by assignment, which means it doesn't make a copy of this object, it just has self point to that original object in memory.

And then we pass in 10 for base and 8.2 for height. Our first line is a simple assignment statement. The right hand side just grabs the value out of our parameter, base, which is 10. The left hand side says to go to our parameter, self, which is an object, and assign its base attribute.

Like a list or dictionary, a user defined object is mutable by default, which means any mutations we make inside a function directly modify the object that was passed in. So we follow self to our empty triangle object, and then we store the attribute, base, on that object, setting it to the value 10.

The next line does essentially the same thing. It stores the value of our height parameter in a new attribute named height. Then we're at the end of the method. So the method call exits, popping off that temporary stack frame and returning execution to where we left off in the program.

Then to finish off our assignment statement, we attach the name big to that object in memory. For this next line of code, we just run the whole thing back. We're creating a new triangle object again. So Python allocates an empty object in memory and then calls the init method, passing in that object for self this time.

And now, when it executes these assignments, it's creating those attributes on that new object, which, when it exits the method, it labels small. All right, we're at this print statement. We evaluate parentheses from the inside out. So Python starts by looking for the name big in memory, and of course, it finds our object.

The dot syntax then tells Python to look for the name height inside of big. And inside big, it finds the attribute height. So we grab that value, 8.2, and substitute it back in, and then print it to the console. Next line, once again, we go to the name, big, but this time inside, we're looking for the name get_area.

Like before, Python starts by checking the attributes for a match. That's why we can't use the same name for an attribute and a method. It doesn't find a match, but Python knows that this object is a triangle type. So it follows that reference to the triangle class and looks for the name, get_area, there.

And lo and behold, it finds this method definition and executes it, and as always, passes in the object the method was called on for that first parameter self. So assignment statement time, we go to the object, self, and then look for the attribute, base, stored on that object, grab its value and substitute it in.

And then we do the same for height and simplify. We store that result in a new local variable area, in our temporary stack frame. In our last line of this method, we divide that area by two and return that value back to the caller. And finally, we print it to the console.

Now we've got our mutator method, scale. Python looks in its memory for the name, small, and then looks for the name, scale. Doesn't find it in any of the attributes, so it follows this reference to the triangle class and it finds the method, scale. So let's execute it.

This time we called scale on the object, small. So that's what gets passed in for self. And for factor, we have the value two. We follow self to our small object over here and grab its attribute, base, substitute that in and multiply by our factor, two. We've simplified our right hand side, so we're ready to store.

Since our object is mutable, we'll follow self over to the object small, and then update its base attribute to store the value, six. And then we do this same process for the height attribute. When we exit the method call, our stack frame goes away, but we directly mutated that original object, small.

So all of our changes remain. Our next line, once again, scales that same object, small, but this time our base and height attributes are different, because we already mutated them in the previous scaling. So we end this method call with an even bigger base and height.

Alas, we're at our last line. So the program terminates and our lovely triangle objects get cleared. So when you're tracing method calls, remember that that first parameter, self, is always the object you're acting on, which you can use to access the object's internal state, and that object is mutable.

So any changes you make inside of the method affect the original object you passed in.

Video © Khan Academy, used under CC BY-NC-SA. Watch on Khan Academy. Puzzicle is not affiliated with Khan Academy.