Chasing simple ideas: Everything is an object

Deep dive into Python Objects

Simple but not easy: Abstraction

Object-oriented programming cannot be explained without abstraction. Beyond that also It is one of the key principles when building software. While it is important to understand and utilize abstraction, it can get in the way of understanding internals. After all, abstractions are put in place to hide details.

In Python, objects are the abstraction for data. To understand them better, we will have to break through this abstraction layer. Observing the behaviour of Python objects is a good start but we need to dive deeper.

Commonly, these concepts are explained by creating classes for cars, animals, or shapes. Here, we won't do that. Instead, we will use the built-in Python types like int and list only.

Code

CPython implementation

We start by taking a look at how CPython implements objects:

typedef struct _object PyObject;

Since AI gets to do the boring work these days, let us ask Cody to explain _object:

The _object struct defines the basic structure of all Python objects. It contains:

  • _PyObject_HEAD_EXTRA: Extra space for additional fields in debug builds.

  • ob_refcnt: The reference count field. This is used to track when the object can be deallocated.

  • ob_type: A pointer to the object's type object. This defines the object's type and its methods.

So in summary, _object defines the basic fields that are common to all Python objects - their reference count and type information.

More specific object types (like lists, dicts, etc.) build upon this basic _object struct by extending it with additional fields. But they always keep these core fields to maintain memory management and type information.

Adventure with objects

Now, that you have seen the basic structure, let us take help from rich to see this in action. You can install and import rich as below:

# Install rich with your favourite package manager
# Replace the default print and inspect
from rich import print
from rich import inspect

Notice the 23 attributes that are not shown, use inspect(object, all=True to see them all. This was still abstract but you get the idea. Now, let us work with actual objects.

Python object has an identity, a type and a value

We can inspect the id, class and value of objects. Here's an example of int:

Objects have many interesting properties, most importantly:

  • 💡
    An object’s identity never changes once it has been created

Question for beginners: Which one is the object here, x or 1? The answer lies in id(x) which is the memory address.

💡
Generally, an object's type is unchangeable*
  • * There are ways to do this assuming you know what you are doing. However, it isn't easy when there are so many ways to shoot yourself in the foot.

💡
The value of some objects can change
  • Notice that y is a reference to a list (a mutable type). Objects whose value cannot be changed are called immutable. Understanding this distinction can save you from a lot of trouble. No wonder, mutable arguments made it to the top of common gotchas.

  • Python wouldn't be fun if we couldn't change values. Without mutability, this would be an adventure in functional programming with Lisp or Haskell.

That's a wrap on Python objects. Now you know how to inspect id, type and values. All the code is here as a notebook: