Python developers love to say that “everything is an object.” And indeed, when I teach Python classes, I say this several times, and many people nod in agreement, assuming that I’m merely repeating something they’ve heard before. After all, people often say that everything in Java is an object (except for the things that aren’t), and that everything in .NET is an object.
But when we say that everything in Python is an object, we really mean everything, including — much to the surprise of my students — classes. This makes enormous sense, and it makes the entire object system easier to understand. And yet, it is still hard to put things in perspective.
In this blog post, I want to walk through some of the connections that we have among objects in Python, in the hopes that it’ll help to cement some of the ideas that stem from this “everything is an object” idea. It’ll also demonstrate some of the fun that happens when you’re creating an object hierarchy, and how things can get a bit weird.
Let’s start with a simple class (MyClass), and a simple instance of that class (m). In Python, we would write:
class MyClass(object): pass m = MyClass()
In Python 3, we don’t need to explicitly say that MyClass inherits from object, since that’s true for all classes. But in Python 2, we have to inherit from object; if we don’t, then we get old-style classes, which we really don’t want.
Let’s see how this looks visually, with the arrow indicating that m is an instance of MyClass:
So far, that’s not very exciting. But let’s remember that everything in Python is an object. Thus, it’s true that m is an instance of MyClass; we can learn this by using the type function:
>>> type(m) __main__.MyClass
What happens if we ask MyClass about its type?
>>> type(MyClass) type
Yes, MyClass is an instance of type — just as str, int, bool, and other Python classes are instances of type. Our diagram has just gotten a bit more complex:
In the above diagram, we see that m is an instance of MyClass, and MyClass is an instance of type.
One main difference between regular objects and classes is that classes have a __bases__ attribute, a tuple, which indicates from which other class(es) this class inherits. MyClass, like all classes, should really have two pointers in our diagram — one representing its type, and another representing from which class (object) it inherits:
Many of the people to whom I teach Python are confused by the distinction between type and object, and what roles they play in the object’s life. Consider this:
- Because MyClass is an instance of type, type.__init__ determines what happens to our class when it is created.
- Because MyClass inherits from object, invoking a method on m will result in first looking for that method on MyClass. If the method doesn’t exist on MyClass, then Python will look on object.
All of this is well and good, but let’s take it a bit further: We know that MyClass is an instance of type. But this means that type itself is a class, right? What is the type of this type class?
>>> type(type) type
Yes, in one of my favorite parts of Python, the type of type is type. In other words, type is an instance of itself. Pretty cool, eh? Let’s see how that fits into our diagram:
If type is a class, then we know it must have two pointers in our diagram — one pointing to its class (type, aka itself), but the one to the class from which it inherits. What does type inherit from?
>>> type.__bases__ (object,)
Let’s thus update our diagram, to show that type inherits from object. This makes sense, since if we invoke str(MyClass), we can rely on the inherited implementation of object.__str__, without having to create a separate type.__str__. And indeed, it would seem that this is what happens:
>>> type.__str__ is object.__str__ True
Let’s now update our diagram to indicate that type inherits from object:
Finally, let’s not neglect our object class. As an object, it too must have a type. And as a class, we know that its type is type. Let’s add that to our diagram:
Remember that object is at the top of our inheritance hierarchy. This is represented in Python by an empty tuple:
>>> object.__bases__ ()
We can represent this in our diagram in the following way:
Finally, let’s see what happens when we add a new class to this hierarchy, subclassing from MyClass. MySubClass inherits from MyClass, but is still an instance of type:
If you’re an experienced Python developer, then the above may well be second nature for you. But if you’re new to the language, and particularly to the ways in which the various objects and classes interact, then I hope this has provided you with some additional clarity. Please let me know if there are additional aspects that you find confusing, and I’ll try to clarify them in future blog posts.
If you liked this explanation, then you’ll likely also enjoy my ebook, “Practice Makes Python,” with 50 exercises meant to improve your Python fluency.
The post Python’s objects and classes — a visual guide appeared first on Lerner Consulting Blog.