This content originally appeared on DEV Community and was authored by Maria Fernanda Peña
Introduction
Python is a powerful and flexible language, but to use it effectively, we need to understand how it handles objects, references, and memory. This post explores key concepts such as object identity, mutability, and how arguments are passed to functions, providing essential knowledge for writing efficient and bug-free Python code.
What is an Object in Python?
In Python, everything is an object: integers, strings, lists, functions, and even classes. Each object has three main properties:
Identity → It is a unique number that identifies it in memory, which can be retrieved using
id(obj)
.Type → Defines what kind of object it is, obtained with
type(obj)
.Value → The actual data stored within the object.
Example:
x = 10 # 10 es un objeto entero
print(id(x)) # Muestra la identidad del objeto
print(type(x)) # Muestra que es un entero
Difference Between a Class and an Object (Instance)
Class: A blueprint for creating objects.
Object (Instance): It is a concrete version created from a class.
Example:
class Dog:
def __init__(self, name):
self.name = name
fido = Dog("Fido") # 'fido' is an instance of the Dog class
Object Identity id()
The id()
function returns the memory address of an object.
Example:
x = 42
print(id(x)) # Displays a number (memory address)
Does id()
Always Stay the Same?
For immutable objects (
int
,float
,str
,tuple
,bool
), modifying them generally creates a new object with a new id. However, due to Python’s interning, small integers (-5 to 256) and some strings may retain the same id.For mutable objects (
list
,dict
,set
), modifying them keeps the id the same
# Immutable object
a = 10
print(id(a)) #Initial ID
a += 1
print(id(a)) # New ID
# Mutable object
lst = [1, 2, 3]
print(id(lst))
lst.append(4)
print(id(lst)) # Same ID
Checking Identity and Equivalence
==
checks if values are equal.is
checks if two variables refer to the same object in memory.
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print(a == b) # True (values are equal)
print(a is b) # True (same memory address)
print(a == c) # True (values are equal)
print(a is c) # False (different objects in memory)
Interning (Memory Optimization)
Python optimizes memory by reusing immutable objects in certain cases.
Integers (-5 to 256)
Small integers (typically between -5 and 256) are interned, meaning they are stored once in memory and reused to improve performance.
a = 89
b = 89
print(a is b) # True (same memory address)
Strings (simple literals)
Python can also apply interning to certain immutable strings, especially when they are short, alphanumeric, and created directly in the code (e.g., s = “hello”). However, dynamically constructed strings (s = “he” + “llo”) or those containing special characters may not be automatically interned.
s1 = "hello"
s2 = "hello"
print(s1 is s2) # True (interning applied)
Mutability
Mutability refers to whether an object’s state can be changed after it is created.
Mutable vs Immutable Objects
A mutable object can change its contents after it is created. An immutable object cannot.
For immutable types, modifying a value usually creates a new object. However, due to interning, Python may reuse small integers and simple strings.
Immutable Types
-
int
,float
,str
,tuple
,bool
,frozenset
,complex
Mutable Types
-
list
,dict
,set
,bytearray
Mutable example:
lst = [1, 2, 3]
lst.append(4)
print(lst) # [1, 2, 3, 4]
Immutable example:
tup = (1, 2, 3)
tup[0] = 100 # ❌ Error! Tuples are immutable
Reassignment vs Mutation
When you reassign a variable, you are not modifying the object itself but rather making the variable point to a new object in memory. This is especially relevant for immutable objects, where any modification results in reassignment rather than mutation.
Mutation
l1 = [1, 2, 3]
l2 = l1 # Both variables point to the same list in memory
l1.append(4) # The same object is modified
print(l2) # [1, 2, 3, 4] ✅
append() modifies the original list, so l2 also reflects the change.
Reassignment
l1 = [1, 2, 3]
l2 = l1
l1 = l1 + [4] # A new list is created, and l1 now points to it
print(l2) # [1, 2, 3] ❌ (l2 still holds the original list)
Here, the original list was not modified; instead, l1 was reassigned to a new list, leaving l2 unchanged.
General Rule
Methods like .append(), .extend(), .remove(), etc., modify the same list in memory.
Operations like
l1 = l1 + [x]
or l1 = l1 * 2
create new lists and reassign the variable.
Reference and Aliasing
References
A reference is a “pointer” to an object’s location in memory. In Python, variables do not store the value directly; instead, they reference an object in memory.
x = [1, 2, 3] # 'x' is a reference to the list
y = x # 'y' refers to the same object as 'x'
Aliasing
Aliasing happens when two variables point to the same object, either directly or within nested data structures.
lista1 = [10, 20, 30]
lista2 = lista1 # lista2 is a reference to lista1
lista2.append(40)
print(lista1) # [10, 20, 30, 40] (both variables point to the same object)
How Python Passes Arguments to Functions
Python passes arguments by object-reference. Immutable objects behave like pass-by-value because modifications create a new object, while mutable objects maintain changes.
Immutable objects → Changes create a new object (pass-by-value behavior).
Mutable objects → Changes affect the original (pass-by-object-reference behavior).
Mutable objects can be modified within functions, but immutable objects cannot.
Immutable Objects – Passed by Value (Copy of Reference)
def modify(x):
x = 100 # Creates a new object
num = 50
modify(num)
print(num) # 50 (unchanged)
Mutable Objects – Passed by Reference
def modify(lst):
lst.append(4) # Modifies the original object
numbers = [1, 2, 3]
modify(numbers)
print(numbers) # [1, 2, 3, 4]
Understanding these concepts in Python is key to writing better code. Keep exploring, and happy coding!
Got insights to share? Drop them in the comments!
This content originally appeared on DEV Community and was authored by Maria Fernanda Peña