For AI agents: use /llms.txt for the Nakafa content index.
In Python, lists are data structures that can be modified after they are created. Let's understand how lists behave when we perform certain operations.
When a list is assigned to a variable, the variable refers to that list object. Assignment does not copy the list. Another variable or container can therefore refer to the very same object.
# Creating list and providing reference
list1 = [0, 1, 2]
list2 = ['a', list1, True]
print(list2) # ['a', [0, 1, 2], True]
# Changing elements in list1
list1[1] = [3, 4, 5]
print(list2) # ['a', [0, [3, 4, 5], 2], True]Notice how changes to list1 also affect list2. This happens because list2 doesn't store a copy of list1, but rather a reference to the same list object in memory.
A full slice creates a new outer list containing references to the same elements. This is a shallow copy: replacing an outer element does not affect the original list, but a nested mutable element remains shared.
# Slice operations create new lists
list1 = [0, 1, 2]
list2 = list1[:] # Taking all elements with slice
print(id(list1)) # Example: 3104
print(id(list2)) # Example: 8864 (different from list1)
# Changing elements in list2 doesn't affect list1
list2[0] = 'x'
print(list1) # [0, 1, 2] (unchanged)
print(list2) # ['x', 1, 2]
# Nested mutable elements are still shared by a shallow copy
nested1 = [[1], [2]]
nested2 = nested1[:]
nested2[0].append(3)
print(nested1) # [[1, 3], [2]]Every object in Python has three important characteristics that distinguish it from other objects.
Identity answers whether two references point to the same object. id() returns an integer that is unique among simultaneously living objects, while is compares identity directly. The language does not promise that the integer is a physical memory address.
a = 300
print(a) # 300
print(type(a)) # <class 'int'>
print(id(a)) # 10120
b = a # b references the same object as a
print(id(b)) # 10120 (same as a)
a = a + 1 # Creates new object
print(id(a)) # 10492 (different from before)Use is when object identity is the question, most commonly in checks such as value is None. Use == when values should be compared. Interpreter caching and constant folding can make equal immutable values share an identity, but application logic must never rely on that implementation detail.
a = [1, 2]
b = a
c = [1, 2]
print(a is b) # True: same object
print(a == b) # True: same value
print(a is c) # False: different objects
print(a == c) # True: equal valuesPython classifies objects based on their ability to be changed after creation.
Immutable objects cannot be changed after creation. Any operation that appears to change the object actually creates a new object.
# Integer is immutable
a = 300
print(id(a)) # 10120
a = a + 1 # Creates new object
print(id(a)) # 10492 (different)
# String is also immutable
str_var = 'hello'
print(id(str_var)) # 75568
# Trying to change string will result in error
# str_var[1] = 'a' # TypeErrorCommon immutable types in Python include:
bool (boolean)int (integer)float (floating point)complex (complex numbers)str (string)tuple (tuple)A tuple's own sequence of references cannot change, although an object referenced by the tuple can still be mutable.
Mutable objects can be changed after creation without creating a new object. The object's identity remains the same even though its value changes.
# List is mutable
list_var = [0, 1, 2]
original_id = id(list_var)
# Changing an element preserves the list's identity
list_var[0] = 9
print(id(list_var) == original_id) # True
# Assignment rebinds the variable to a new object
list_var = [0, 0, 0]
print(id(list_var) == original_id) # False
# Slice assignment mutates the current list in place
current_id = id(list_var)
list_var[:] = ['a', 'b', 'c']
print(id(list_var) == current_id) # TrueCommon mutable types in Python include:
list (list)dict (dictionary)set (set)It's important to distinguish between assignment (giving new value) and in-place modification.
list1 = [0, 1, 2]
list2 = list1 # Both variables reference the same object
print(id(list1)) # 700
print(id(list2)) # 700 (same)
# In-place modification affects both variables
list1[1] = 'x'
print(list1) # [0, 'x', 2]
print(list2) # [0, 'x', 2] (also changed)
# Assignment creates new reference
list1 = [1, 2, 3]
print(list1) # [1, 2, 3]
print(list2) # [0, 'x', 2] (unchanged)Mutability determines whether an operation changes an existing object or rebinds a name to another object. Immutability removes one source of shared-state bugs, but it does not by itself make a program thread-safe. Referenced mutable objects, compound operations, and external resources still require deliberate synchronization.