Join us and get access to thousands of tutorials and a community of expert Pythonistas.
This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.
Introspecting Objects
00:00 In the previous lesson, I showed you the two built-in functions that allow you to interact with Python’s scope. In this lesson, I’ll cover three functions that give you information about an object.
00:10
There are three built-in functions that allow you to poke and prod at an object to see information about its makeup. id() returns a unique identifier, allowing you to determine whether two references point to the same thing.
00:23
dir() returns a list containing the names of an object’s attributes, and vars() returns the .__dict__ attribute for an item. Let’s visit the REPL to see these in action. Every object in Python has to live somewhere, and the built-in id() function gives you a unique identifier for it.
00:42 This allows you to distinguish between objects with the same contents versus references to the same object.
00:50
Just starting with a variable, and when I call id() on it, I get a number. If you’re coding along with me, your number is going to be different.
01:00 In fact, it isn’t even guaranteed to be the same on subsequent runs. This has to do with how Python manages memory.
01:10
Since dog is a different object than cat, it has a different id(). They’re not even that close to each other. It isn’t sequential.
01:19
Remember though, Python is actually using references to the objects in the system, so that’s related to the unique id() of it. If I add a new variable pointing to cat,
01:31
calling id() shows that it is referencing the same object. It has the same id(). Of course, at this point, you’re probably expecting me to say something about everything being an object, and well, you’d be right.
01:44
Even a class is an object. So even a class has an id() to go with it.
01:51
Using id() can reveal some surprising things about Python internals. The id()s here are the same. For some static data, like short strings and small integers, Python saves memory by only keeping one copy of the object.
02:05 So although they’re immutable, and I’ve technically constructed two strings here, one for each time I called the function, the second construction is actually returning a reference to the first one.
02:16
Python gets away with this because strings are immutable. You can’t actually edit "Red", and if you did, what would happen is you’d get a new string that was the edited copy.
02:26
So if I have two variables each pointing to "Red" and then edit one of them, the second isn’t affected because the one I edited is pointing to a new object.
02:35
This is more an implementation detail than anything else, but an interesting little peek under the covers. Most of the time when I’m using id(), it’s to help debug.
02:44
Sometimes when dealing with objects, you want to create a new one, and sometimes you just want a reference to it. Knowing the id() allows you to differentiate between these two situations.
02:55 The next two functions help you see what belongs to an object. To demonstrate this, I need an object. Bear with me here.
03:33
Okay, now that I have a class, let’s take a look at it. Calling dir() returns a list of the names of everything defined on the object, which in this case is the class.
03:43
There’s a fair amount here, which all comes from the base object class, and most of them are dunder methods. At the end of the list, you see our two methods on the class, area and perimeter.
03:54 Let me create an instance of a
04:00
Rectangle. And dir() on this contains a bit more. You get all of those same dunder methods, because an instance object of a class also has all those things from the class, but on top of it, you also get the attributes defined in the .__init__() call, length and width in this case.
04:16
You’ll recall from earlier lessons that Python allows you to dynamically add and change attributes on an object. It implements this feature by using a dictionary called .__dict__.
04:26
Not all objects have this dictionary, but most do. Calling vars() shows you the contents of that dictionary,
04:34
which for our Rectangle instance contains our length and width attributes and their corresponding values.
04:42
When you call it on the Rectangle class, you get a lot more, including a bunch of dunder methods, the docstring, and in fact it also has .__dict__ where length and width got stored for the instance objects in the first place.
04:54
The return type of vars() is a mapping proxy, which is a special read-only reference to a dictionary, so you can’t actually muck with it directly.
05:04
The results from dir() are a little different depending on what you call it with. Let me just highlight those differences.
05:10 With no arguments in the REPL, you get the global scope.
05:16
When you call dir() on a module, you get what is defined at the module level, which typically is mostly the functions or classes within that module.
05:25
Here, you’re seeing all the functions inside of the math module.
05:33
You already saw me use dir() on an object, giving you what is defined on the object, including everything from the base class.
05:41
And for special cases like built-in functions, absolute value being the one shown here, you get something similar to the base object. Just some of the values are a little different.
05:53 Next up, something you probably shouldn’t do unless you’re very careful: executing code from string data.
Become a Member to join the conversation.