Python Variables and Object References

Learn how Python variables work as references to objects, not containers. Understand object identity, the id() function, and how assignment and mutation behave.

5 min read

Python variables are one of the first concepts you learn, but the mental model you build in those first few minutes shapes how you understand every line of code you will ever write. In many programming languages, a variable is a named box that holds a value: you put a number in the box, and later you can take it out, replace it, or read it. Python does not work that way, and if you carry the box metaphor into Python, you will eventually encounter behavior that seems confusing: a list changes after you pass it to a function, or an integer does not change when you expected it to.

Python variables are references to objects, not containers for values. The distinction matters because it explains why some assignments behave one way and others behave differently. Once you internalize the reference model, the behavior of reassignment, mutation, function arguments, and copy operations all become predictable. If you have read the earlier article on Python variables, this article explains the mechanism that powers all assignment operations.

Variables are labels, not boxes

Think of a Python variable as a sticky note with a name written on it, and think of the value as an object living somewhere in the computer's memory. When you write x = 42, Python creates an integer object with the value 42 and attaches the sticky note labeled x to it. The variable does not contain 42; it points to the object that does.

This model explains what happens during assignment between variables. Writing y = x does not copy the value from x into y. It attaches a second sticky note, labeled y, to the same object that x already points to. Both names now reference the exact same integer. If you later write x = 99, Python creates a brand-new integer for 99 and moves the x sticky note to it. The y sticky note stays right where it was, still pointing to the original 42 object. Reassignment moves one label at a time and never affects the underlying object.

The built-in function id() returns a unique integer for each object in memory. In CPython, the standard Python implementation, this is the actual memory address. Comparing id() values is the most direct way to confirm whether two variables point to the same object:

pythonpython
x = 42
y = x
print(id(x) == id(y))   # True: both reference the same object
 
x = 99
print(y)                 # 42: y still points to the original
print(id(x) == id(y))    # False: now they reference different objects

Seeing those id() values change when you reassign a variable makes the reference model concrete rather than abstract. The variable is just a name; the object is what actually holds the data.

Immutable and mutable objects

The key to predicting Python's behavior is knowing whether an object is immutable or mutable. An immutable object cannot be changed after creation. Integers, floats, strings, tuples, and booleans are all immutable. Any operation that looks like it modifies an immutable object actually creates a brand-new object and returns it. A mutable object can be changed in place, and every variable pointing to that object sees the change.

This table summarizes which common types fall into each category:

Immutable (cannot change in place)Mutable (can change in place)
int, float, boollist
str (string)dict (dictionary)
tupleset
None, frozensetMost user-defined classes

When you modify a mutable object through one variable, every other variable that references the same object sees the change because there is still only one object, and it has been altered:

pythonpython
scores = [85, 92, 78]
backup = scores            # backup points to the SAME list
scores.append(95)          # Mutates the list in place
print(backup)              # [85, 92, 78, 95]: backup sees the change

This is the single most common source of surprise for Python beginners. Assigning a list to a second variable does not copy it; it creates another reference. If you need an independent copy, use the .copy() method, a full slice like backup = scores[:], or the copy module for nested structures. The article on common variable mistakes covers more scenarios where this behavior trips up newcomers.

The is operator and object identity

Python provides two different ways to compare variables. The double-equals operator checks whether two objects have the same value. The is keyword checks whether two variables point to the exact same object in memory. This distinction is important because two separate objects can be equal in value without being identical in identity.

The is operator is most commonly used to check for None, since there is only one None object in the entire Python runtime. Writing if result is None is the idiomatic Python pattern you will see in every codebase. It is faster and semantically more precise than comparing with double-equals, and it cannot be overridden by a custom equality method on an object the way double-equals can.

How this connects to the rest of Python

The reference model is not an isolated curiosity; it underpins how function arguments work in Python. When you pass a variable to a function, Python passes the reference to the object, not a copy of the object. A function can mutate a mutable argument and the caller will see the change, but a function cannot reassign the caller's variable to a new object. That behavior, often called pass-by-object-reference, is directly explained by the label model described in this article.

The reference model also explains why data types are categorized as mutable or immutable, why you need to think about shallow versus deep copies when working with nested collections, and why default mutable arguments in function definitions behave the way they do. Every one of those topics builds on the simple idea that a variable is a name attached to an object, not a box that stores a value directly.

Rune AI

Rune AI

Key Insights

  • Python variables are labels that point to objects, not boxes that store values.
  • Use id() to see which object a variable refers to at any moment.
  • Reassigning a variable points it to a new object and never modifies the original.
  • Mutable objects can be changed in place, and all references see the change.
  • Immutable objects cannot change; reassignment always creates a new object.
  • The is operator checks object identity, while == checks value equality.
RunePowered by Rune AI

Frequently Asked Questions

Are Python variables boxes that store values?

No. A Python variable is a name that points to an object in memory. The object holds the value, and the variable is just a label for that object. This is different from languages like C or Java where a variable is a fixed memory location. Understanding this explains why list mutation affects multiple variables but integer reassignment does not.

What does id() return in Python?

The id() function returns an integer unique to an object during its lifetime. In CPython, this integer is the object's memory address. It is most useful for checking whether two variables point to the exact same object, not for comparing values.

Why does reassigning an integer not change other variables pointing to the same integer?

Integers are immutable in Python. When you reassign a variable, you point it to a brand-new integer object rather than modifying the existing one. Any other variable still points to the original integer, unchanged. This is why x = 5 followed by y = x and then x = 10 leaves y still referencing 5.

Conclusion

Python variables are names that reference objects, not containers that hold values directly. This reference model explains why mutation of mutable objects is visible through multiple names while reassignment of immutable objects only affects one name. Understanding identity, equality, and mutability is foundational for function arguments, class design, and every Python concept that follows.