Variables are references, objects live on the heap, and CPython frees them with reference counting plus a cycle collector. Mutability explains most “weird” Python bugs.
Names point to objects
a = [1, 2]
b = a # b refers to the SAME list
b.append(3)
print(a, a is b) # [1, 2, 3] True
x = 5
y = x
y += 1 # ints are immutable: y now points to a new object
print(x, y) # 5 6| Immutable (can't change in place) | Mutable (can change in place) |
|---|---|
int, float, bool, str, bytes | list, dict, set, bytearray |
tuple, frozenset, None | Most user-defined objects |
+= differs for mutable and immutable types
nums = [1]
alias = nums
nums += [2] # list: extends in place
print(alias) # [1, 2]
s = "a"
alias_s = s
s += "b" # str: builds a new object
print(alias_s, s) # a abReference counting & the cycle collector
Reference counting
Every object tracks how many references point to it. When the count hits 0, it is freed immediately.
Cycle detection
Objects that reference each other (a ↔ b) never reach 0. The
gcmodule periodically finds and frees such unreachable cycles.Generations
New objects are checked often; objects that survive are promoted and checked less — most objects die young.
import sys, gc
data = []
# getrefcount's own argument adds a temporary reference:
print(sys.getrefcount(data) >= 2) # True
class Node:
def __init__(self):
self.other = None
a, b = Node(), Node()
a.other, b.other = b, a # a reference cycle
del a, b
print(gc.collect() >= 2) # True