Everything Is an Object
Understanding the Python Object Model
Introduction
“Objects are a way of packaging behavior with data.” — Alan Kay
Python is often described with the phrase “everything is an object”. This is not a slogan or a teaching shortcut. It is a precise statement about how the language works internally. Integers, strings, functions, classes, modules, and even types themselves all follow the same fundamental structure. Understanding the Python object model gives you a clearer mental model of identity, mutability, method calls, and performance.
What an Object Really Is in Python
At runtime, every Python object is a block of memory managed by the interpreter. That block contains three essential pieces of information. First is a reference count used for memory management. Second is a pointer to the object’s type. Third is the object’s actual data.
This structure is defined in C inside the interpreter. Conceptually, you can think of every object as having an identity, a type, and a value. The identity corresponds to its address in memory. The type determines what operations are valid. The value is the data the object represents.
In Python, variables are not containers for data, but rather references (pointers) to objects stored in memory. Each object in the CPython implementation has a metadata structure containing a reference count pointer and other essential “caratula” data.
my_list = [’string’, 42]Identity, Type, and Value
You can observe these three properties directly.
x = 42
print(id(x))
print(type(x))
print(x)
<memory address>
<class ‘int’>
42The id function returns a unique identifier for the object during its lifetime. The type function returns the object’s type. The value is what you normally think of as the object’s contents.
Identity never changes. Type never changes. Value may change if the object is mutable.
Every item of data in Python is an object, and each object possesses three core properties: a unique identity, a specific type, and an associated value.
Identity
An object’s identity is a unique, unchangeable integer identifier that distinguishes it from all other objects during its lifetime. You can think of it as the object’s address in memory.
Function: Use the built-in
id()function to retrieve an object’s identity.Operator: The
isoperator is used to compare the identities of two objects.x is yreturnsTrueif and only ifxandyrefer to the exact same object in memory.
x = [1, 2, 3] # Allocates list at address A
y = [1, 2, 3] # Allocates NEW list at address B
z = x # Copies the pointer to address A
# Printing IDs (Memory Addresses)
print(id(x)) # e.g., 43001000 (Address A)
print(id(y)) # e.g., 43002000 (Address B - different from x)
print(id(z)) # e.g., 43001000 (Address A - same as x)
# Comparing Identity (Pointer Comparison)
print(x is y) # False (Address A != Address B)
print(x is z) # True (Address A == Address A)
# Comparing Value (Content Comparison)
print(x == y) # True (Contents are the same: [1, 2, 3])Why this happens (The Low-Level View)
Heap Allocation: When you define
x = [1, 2, 3], Python allocates a new list object on the heap and givesxa pointer to it.Distinct Pointers: When you define
y = [1, 2, 3], Python allocates a second, distinct list object on the heap at a different address.Aliasing:
z = xcopies the pointer, not the data. Bothzandxpoint to the same heap address.
is compares the pointers (memory addresses). == calls the __eq__ method to compare the actual data.
Type
An object’s type (or class) defines the kind of data it represents and the operations that can be performed on it. The type also determines whether an object is mutable (can be changed after creation) or immutable (cannot be changed after creation). Like its identity, an object’s type is fixed once created.
Function: Use the built-in
type()function to determine an object’s type.
num = 10
greeting = “Hello”
my_list = [1, 2]
print(type(num)) # <class ‘int’>
print(type(greeting))# <class ‘str’>
print(type(my_list)) # <class ‘list’>Value
An object’s value is the actual data or content stored within it.
Operator: The
==operator is used to compare the values of two objects.x == yreturnsTrueif the contents ofxandyare the same, regardless of whether they are the exact same object in memory.
x = [1, 2, 3]
y = [1, 2, 3]
# x and y have the same value but different identities (as shown above)
print(x == y) # True (values are equal)
print(x is y) # False (identities are different)Virtually everything in Python is an object, and these objects reside in the heap. Heap memory is a dedicated, private memory area where all objects and dynamic data structures (like lists, dictionaries, class instances, etc.) are stored. It provides flexible, on-demand memory allocation during a program’s runtime, in contrast to stack memory which is used for function calls and local variables with a fixed size.
Names Are Not Objects
One of the most important ideas in the Python object model is that variables are not containers. Names simply refer to objects.
a = [1, 2, 3] # Initialize list ‘a’ with elements 1, 2, 3
b = a # ‘b’ now points to the exact same list object as ‘a’ (aliasing)
b.append(4) # Modifies the shared list by adding 4 to the end
print(a) # Prints the content of ‘a’, which is now [1, 2, 3, 4]Both names refer to the same object. There was never a copy. Assignment only binds a name to an object. This explains many behaviors that confuse beginners and also underpins how Python passes arguments to functions.
“Variable” vs. “Object” Land
In Python, variables are references that point to objects, rather than containers themselves. You can think of a variable as an arrow connecting a name in “variable land” to a specific object in “object land.” The term “pointer” here is simple and lacks the complex operations (like explicit dereferencing) found in other languages.
This above diagram represents the state of a Python process after running:
numbers = [2, 1, 3, 4, 7]
numbers2 = [11, 18, 29]
name = “Trey"Assignment statements point a variable to an object. After running:
numbers = [2, 1, 3, 4, 7]
numbers2 = numbers
name = “Trey”The state of our variables + objects would be:
𝐋𝐞𝐚𝐫𝐧 𝐭𝐨 𝐛𝐮𝐢𝐥𝐝 𝐆𝐢𝐭, 𝐃𝐨𝐜𝐤𝐞𝐫, 𝐑𝐞𝐝𝐢𝐬, 𝐇𝐓𝐓𝐏 𝐬𝐞𝐫𝐯𝐞𝐫𝐬, 𝐚𝐧𝐝 𝐜𝐨𝐦𝐩𝐢𝐥𝐞𝐫𝐬, 𝐟𝐫𝐨𝐦 𝐬𝐜𝐫𝐚𝐭𝐜𝐡. Get 40% OFF CodeCrafters: https://app.codecrafters.io/join?via=the-coding-gopher
Mutability and Immutability
Objects in Python are either mutable or immutable. Mutable objects can change their internal state without changing identity. Immutable objects cannot.
Lists, dictionaries, and sets are mutable. Integers, floats, strings, and tuples are immutable.
# Assign the integer 10 to the variable x
x = 10
# Assign the current value of x (which is 10) to y.
# y now has its own copy of the value 10.
y = x
# Increment x by 1 (x becomes 11). This does not affect y.
x += 1
# Print the current value of x (11)
print(x)
# Print the value of y (which is still 10)
print(y)The integer object representing ten was never modified. A new integer object was created and x was rebound to it. y still refers to the original object.
Reference Counting and Lifetime
Python primarily uses reference counting to manage memory. Each object keeps track of how many references point to it. When that count drops to zero, the object is destroyed immediately. This immediate destruction explains why resources such as files are often released deterministically in CPython. It also explains why reference cycles require special handling by a cycle detector.
import sys
# Create a new empty list object.
# The reference count is 1 (the variable ‘x’ points to the object).
x = []
# Print the reference count.
# The output will be 2.
#
# BREAKDOWN:
# 1. The variable ‘x’ in the current scope.
# 2. The argument passed to sys.getrefcount() creates a temporary reference
# on the function’s stack frame while it executes.
print(sys.getrefcount(x))The
sys.getrefcount()function always returns a value one higher than the actual number of references to an object because it counts the temporary reference created when the object is passed as an argument to the function itself.
Everything Has a Type
Types themselves are objects. The type of an integer is an object. The type of a class is an object. Even the type of type is an object.
print(type(1)) # “<class ‘int’>”; type of the integer object 1.
print(type(int)) # “<class ‘type’>”; type of built-in int class itself.
print(type(type)) # “<class ‘type’>”; type is a metaclass.This circular looking structure is intentional. It allows Python to treat types uniformly and enables powerful introspection and meta-programming features.
Attribute Access and Method Binding
When you access an attribute on an object, Python follows a well-defined lookup process. It first checks the instance dictionary. If not found, it checks the class. If still not found, it walks the method resolution order through base classes.
When you access a function through an instance, Python automatically binds the instance as the first argument.
class Counter:
def inc(self):
self.value += 1
c = Counter()
c.value = 0
c.inc()The method inc is a function stored on the class. When accessed through c, it becomes a bound method with c passed as self.
The Object Model and Performance
The uniform object model makes Python flexible but not free. Every operation involves pointer dereferencing, type checks, and dynamic dispatch. Even adding two integers requires method lookup and object creation.
This is why Python pushes performance critical work into C extensions. Native code bypasses much of the object machinery and operates directly on raw data.
Why This Model Matters
The Python object model explains why Python behaves the way it does under the hood. It explains aliasing bugs, unexpected mutations, reference leaks, and performance characteristics. It also explains why Python is easy to introspect and extend.
TL;DR.
“Everything is an object” means that all data entities within a Python program are instances of some class. These objects possess both state (data or attributes) and behavior (functions or methods).
Primitive Types are Objects: Unlike some other programming languages where simple data types like integers and strings are primitives, in Python, they are objects. For example, the number
5is an instance of theintclass, and you can call methods on it, such as(5).bit_length().Functions are Objects: Functions themselves can be assigned to variables, passed as arguments to other functions, and even have attributes. This is a core aspect of Python’s support for functional programming paradigms.
Classes are Objects: The definition of a class is also an object (specifically, an instance of a “metaclass”). This enables techniques like decorators and metaclass programming.
Modules are Objects: When you import a module, it’s an object with attributes (the functions and classes it defines).
Consistent Object Model: This consistent approach provides a uniform way to interact with all parts of the language. You can use standard functions like
type()andid()on anything to see its type and unique identifier, respectively.












yayyyy more python content