Neural DownloadNEURAL DOWNLOAD
← cd ../blog

The Secret Compiler Inside Python

Python compiles to bytecode before interpreting it. See the hidden compiler, the dis module, and what actually runs when you hit Enter.

pythonpython bytecodepython compilerpython interpretedcpythonpython virtual machine
Share

Python is an interpreted language. You've heard it a thousand times. You write code, Python reads it line by line, runs it. No compilation. No binary. Just text in, results out.

Except that's not what happens. Not even close.

import dis

def add(a, b):
    return a + b

dis.dis(add)
LOAD_FAST    a
LOAD_FAST    b
BINARY_OP    ADD
RETURN_VALUE

That's bytecode. Compiled bytecode. Python's compiler turned your function into low-level instructions *before* it ever ran a single line. And those __pycache__ directories you've been ignoring? They're full of .pyc files — compiled bytecode cached on disk so Python doesn't recompile next time.

Python has a compiler. It always has.

The Hidden Pipeline

When you run python script.py, your source code goes through three compilation stages — all hidden, all automatic:

  1. Tokenizer — rips your code apart. Every keyword, variable, and operator becomes a labeled token. def, name:add, open_paren...
  2. Parser — assembles tokens into an Abstract Syntax Tree. Flat text becomes hierarchy: the function contains a return, the return contains an addition, the addition has two operands.
  3. Compiler — walks the AST and emits bytecode. A compact sequence of instructions for a virtual machine. Not machine code. Not native CPU instructions. But real compiled output.

Three stages of compilation, hidden behind a single command.

The Stack Machine

The bytecode needs something to run it. That's the CPython virtual machine — a stack-based interpreter.

Everything goes through a stack. Values go on, values come off. Here's our add function, step by step:

InstructionStack (after)
LOAD_FAST a[5]
LOAD_FAST b[5, 3]
BINARY_OP ADD[8]
RETURN_VALUE[] (returns 8)

Four instructions. The VM doesn't read your text and figure things out on the fly. It executes pre-compiled bytecode instruction by instruction. Compiled, then interpreted. Both.

Why It's Still Slow

If Python compiles code, why is it slower than C? Because of what happens after compilation.

When C compiles, the output is native machine code. Your CPU runs it directly — an add instruction executes in nanoseconds. Python's compiler targets a *virtual machine*, not your CPU. Every bytecode instruction goes through layers of software overhead:

StepWhat Happens
FetchRead the next bytecode instruction
DecodeDetermine which operation to perform
DispatchJump to the handler in the interpreter loop
Type checkIs this an integer? A float? A string?
UnboxExtract the raw value from Python's object wrapper
ComputePerform the actual operation
BoxWrap the result back into a Python object

What C does directly in hardware, Python does through software indirection at every step. The cost isn't the lack of compilation — it's the overhead of a virtual machine sitting between your bytecode and the CPU.

Calling Python "interpreted" misses the point. It compiles your code into bytecode, then a VM interprets that bytecode. The real question isn't *whether* Python compiles — it's what it compiles *to*.

Watch the full animated breakdown: Python Is NOT an Interpreted Language