Python’s objects and classes — a visual guide

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:

Python objectsSo 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:

Python objects 2

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:

Python objects 3Many 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:

Python objects 4

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:

Python objects 5

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:

Python objects 6Remember 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:

Python objects 7Finally, 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:

Python objects 8

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.

16 thoughts on “Python’s objects and classes — a visual guide”

  1. I really like your graphic explanation. Although I knew the terms, this presentation makes it very easy to see. I think this style of diagrams will be even more valuable when you start to think about metaclasses.

    1. There aren’t really variables in Python. There are names. And they are strings, which, of course, are objects. And as Rueven says, are keys in a namespace dict

    1. All Python classes are instances of type. That’s just by definition. So type(str) is type, and type(int) is type. Every class is an instance of the type class. Indeed, just as you can create a new int by invoking int() and a new str by invoking str(), you can create a new class by invoking type(). Weird and wild, but true!

      So every class is an instance of type. But classes have a separate attribute (__bases__) indicating from whom they inherit. You might have 100 different classes in your system. They’ll all inherit from different classes, depending on how you’ve defined them. But unless you use metaclasses, 100% of your defined classes will be instances of type.

      Does this make sense?

  2. One thing that doesn’t make sense to me is your favorite part of python “type of type is type”.

    As far as I know class and instance are two different things.
    so the type class and the type instance in python should be two different things, indeed the name with which we refer to the type class and type instance are same!

    yet (type(type) is type) == True
    and (type.__class__ is type) == True
    Don’t get it!
    How the class and instance are happen to be same?
    Great Blog though…

    1. When we say that everything in Python is an object, what we’re really saying is that everything in Python is an instance of a class. So “abc” is an instance of str, and 123 is an instance of int.

      In many other languages, that’s where it ends: You have instances and classes, and that’s that.

      But in Python, our classes are also objects, which means they’re also instances. But instances of what? It turns out that str and int are both instances of type.

      As an analogy, imagine that you have a bunch of cars. Each car is made at a factory. But the factory was also made by someone or something, right?

      So if type() creates classes, then what creates type? That’s the magic, and the weirdness: type creates classes, and type is a class, thus type creates itself ! In other words, the type of type is type. Weird, but true.

  3. What advantage does Python gets by making classes an instantce of type over other languages where classes aren’t instances?

    1. First of all, the fact that classes are regular objects that can be passed to functions and stored in dictionaries makes the language simpler to understand, simpler to implement, and more flexible.

      When you invoke int(‘5’), you’re not casting 5 to be an integer. Rather, you’re creating a new instance of int, based on the string ‘5’. Invoking a class in the way you do a function is very standard in Python, and exists because classes (like functions) are “callable.” Thus, invoking int(‘5’) works in the same as calling len(‘abc’) — we’re invoking the callable protocol on an object. The fact that one object is a class, and another is a function, is irrelevant.

      The moment you say that classes are special, you’re ensuring that the implementation of the language will be more complex, and that it’ll be harder for people to understand. At least, I think so!

  4. One thing you don’t mention: where does the attribute __bases__ come from.

    dir(MyClass) and dir(object) do not reveal this attribute

    It wasn’t until Abhisek asked about “type” and you explained that “type creates classes, and type is a class” that I thought to check type’s attributes for __bases__ wherein I was rewarded with my answer.

    1. The attributes for instances of class A are set by A.__init__. Or, if the attribute isn’t set on the instance, A might have the attribute.

      So the attributes for an instance of class “type” are set by type.__init__, or are on type itself. Because MyClass is a class, and thus an instance of type, __bases__ is set by type.__init__ for new classes; if it isn’t set, then type.__bases__ is the default, which is (). Or so I’m assuming!

  5. If I do:
    >> dir(MyClass)
    I can’t find the attribute __bases__ in the list.
    Why ? Doesn’t dir provide all attributes and methods from an object ?
    But:
    >> MyClass.__bases__
    provides (,) this is correct

Leave a Reply

Your email address will not be published. Required fields are marked *

− one = 1