runtime
180 / 250
lesson 180 Memory & Python Internals · advanced

The interpreter: bytecode and the GIL

CPython compiles your code to bytecode and executes it one instruction at a time, holding the Global Interpreter Lock. That explains why one line can be several steps, and why threads behave as they do.

You'll be able to
  • Identify the interpreter and version with sys.implementation, sys.version_info and platform
  • List a function's bytecode instructions with dis.get_instructions
  • Explain the GIL, why x += 1 is not atomic, and what free-threaded builds change
The idea

"Python" is a language; the program that runs it is an implementation. The standard one, CPython, is written in C. Others exist (PyPy, MicroPython), and this browser runs CPython compiled to WebAssembly (Pyodide). sys.implementation.name, sys.version_info and platform.python_implementation() say which one you have, which matters because memory behaviour and threading differ between them.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · Which interpreter is this?
import platform
import sys
import sysconfig

print(sys.implementation.name, sys.version_info[:2], platform.python_implementation())
print(sys.getswitchinterval())
print(sysconfig.get_config_var("Py_GIL_DISABLED"))

CPython first compiles each function to bytecode: small instructions for its virtual machine, such as *load a value*, *add*, *store*. The dis module shows them. dis.get_instructions(func) yields one object per instruction with an opname and an argval (the name or constant it works on). Exact instruction names change between Python versions, so read them for their meaning, not their spelling.

example-2.py · One line, many instructions
import dis

def record_sale(totals, amount):
    totals["sales"] += amount

steps = [ins.opname for ins in dis.get_instructions(record_sale)]
print(steps)
print([ins.argval for ins in dis.get_instructions(record_sale) if ins.opname.startswith("LOAD")])

The Global Interpreter Lock (GIL) is a lock inside standard CPython: a thread must hold it to execute bytecode, so only one thread runs Python bytecode at a time. Every few milliseconds (the switch interval), and whenever a thread waits for I/O, the GIL can pass to another thread, between two instructions. So:

  • The GIL keeps CPython's own data (reference counts, lists, dictionaries) consistent, which is why a single list.append from two threads doesn't corrupt the list.
  • It does not make your read-modify-write steps atomic: another thread can run between the read and the write of totals["sales"] += amount, and one sale is lost. That needs a lock (see Threading & Multiprocessing).
  • Threads still help with waiting (network, disk), because a waiting thread releases the GIL; they don't speed up pure Python computation, because only one thread computes at a time. Processes do (see Threading & Multiprocessing).

CPython 3.13 added an optional free-threaded build without the GIL (PEP 703). It can run threads truly in parallel, which makes locks around shared data even more important.

When to look at bytecode: to understand why an operation isn't atomic, to see which global names a function really touches, or to compare two ways of writing the same thing. Mistakes to avoid: relying on exact instruction names across versions; believing the GIL makes your own updates thread-safe; and expecting threads to make CPU-bound Python faster on a standard build.

Review
  • sys.implementation, sys.version_info and platform say which interpreter runs your code.
  • CPython runs bytecode; dis.get_instructions shows one line becoming several instructions.
  • The GIL lets one thread run bytecode at a time and may switch between instructions: x += 1 is not atomic.
  • Next module: measuring speed (timeit, cProfile) and making programs faster.
Practise this

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

The interpreter: bytecode and the GIL — Memory & Python Internals · Runtime