import dis
defouter():
x = 10definner():
nonlocal x
x += 1return x
return inner
print("outer bytecode:")
dis.dis(outer)
print("\ninner bytecode:")
dis.dis(outer())
# outer stores x as a cell variable (LOAD_DEREF/STORE_DEREF)# inner accesses x as a free variable (also LOAD_DEREF/STORE_DEREF)# Both use the same cell object, enabling shared state
fn = outer()
print(f"\nFree vars: {fn.__code__.co_freevars}")
print(f"Closure: {fn.__closure__}")
print(f"Cell contents: {fn.__closure__[0].cell_contents}")
Output
Click "Run" to execute your code
nonlocal variables use cell objects shared between the enclosing and inner function. Both access the cell via LOAD_DEREF/STORE_DEREF opcodes. The cell is stored in __closure__.
Challenge
Try modifying the code above to explore different behaviors. Can you extend the example to handle a new use case?