Writing efficient and bug-free Python code becomes much easier once you understand what happens under the hood. Every value you create lives as an object in memory, variables are pointers to those objects rather than containers for raw data, and Python manages memory automatically through reference counting and garbage collection. This page demystifies Python’s memory model so you can reason confidently about object identity, mutable vs. immutable types, and why is and == sometimes give surprising results.
Mutability vs. Immutability
Every variable in Python is a reference pointing to an object in memory. Objects fall into two categories:
Mutable Objects
A mutable object can be changed after it is created. Its memory address stays the same even after modification:
Common mutable types: list, dict, set
Immutable Objects
An immutable object cannot be changed. Any “modification” creates a new object at a new memory address:
Common immutable types: int, float, str, tuple, bool
Tuples are immutable, but if a tuple contains a mutable object like a list, the list’s contents can still be modified:The tuple itself didn’t change (it still references the same list), but the list’s contents did.
Everything is an Object
In Python, every value is a first-class object, including integers, strings, functions, modules, and classes. Each object carries three things:
- A Value — the data itself.
- A Type — defines what the object can do.
- An Identity — a unique integer from
id() representing the memory address.
Identity (id()) vs. Equality (==)
This distinction is one of the most important in Python:
== (equality): compares values — calls __eq__ internally.
is (identity): compares memory addresses — checks if two variables point to the exact same object.
Integer Caching: CPython caches integers from -5 to 256 for performance. This means small integers with the same value share the same object:Never rely on is for integer comparisons. Use == instead.
Reference Counting
Python manages memory automatically. Its primary mechanism is reference counting:
- Every object tracks how many variables reference it.
- When you assign an object to a variable, its reference count increases by 1.
- When a variable goes out of scope or is reassigned, the count decreases by 1.
- When the count reaches 0, Python destroys the object and frees its memory.
Circular references — where Object A references Object B and Object B references Object A — can prevent reference counts from ever reaching zero. Python detects and cleans these up using a secondary Generational Garbage Collector. You can trigger it manually with gc.collect() from the gc module.
Summary