What is CPython and how does the Python compilation process work?
CPython is the reference implementation of Python, written in C. When you type python, you are almost always running CPython. Other implementations include PyPy (JIT-compiled), Jython (JVM), and IronPython (.NET).
The compilation process:
- Source code (.py) is read by the parser.
- Abstract Syntax Tree (AST) is generated from the parsed source.
- Bytecode (.pyc) is compiled from the AST. This is an intermediate representation, not machine code.
- Python Virtual Machine (PVM) executes the bytecode instruction by instruction.
Bytecode is cached in __pycache__/ directories as .pyc files. Python recompiles only when the source file is newer than the cached bytecode.
The PVM is a stack-based virtual machine: operations push and pop values on an evaluation stack.
1. source.py -> Lexer/Parser -> AST
2. AST -> Compiler -> Bytecode (.pyc)
3. Bytecode -> PVM (stack-based VM) -> Execution
4. Bytecode is cached in __pycache__/
5. Recompiled only when .py is newer than .pyc
import dis
import ast
import sys
# 1. View the AST
source = "x = 1 + 2"
tree = ast.parse(source)
print(ast.dump(tree, indent=2))
# Module(body=[
# Assign(targets=[Name(id='x')],
# value=BinOp(left=Constant(value=1), op=Add(), right=Constant(value=2)))])
# 2. View bytecode
def add(a, b):
return a + b
dis.dis(add)
# 2 0 LOAD_FAST 0 (a)
# 2 LOAD_FAST 1 (b)
# 4 BINARY_ADD
# 6 RETURN_VALUE
# 3. Code object internals
code = add.__code__
print(f"Name: {code.co_name}") # add
print(f"Arg count: {code.co_argcount}") # 2
print(f"Constants: {code.co_consts}") # (None,)
print(f"Local vars: {code.co_varnames}") # ('a', 'b')
print(f"Bytecode: {code.co_code}") # Raw bytes
print(f"Stack size: {code.co_stacksize}") # Max stack depth
# 4. Compile manually
code_obj = compile('print("hello")', '<string>', 'exec')
exec(code_obj) # hello
# 5. Check Python implementation
print(sys.implementation.name) # cpython
print(sys.version) # 3.x.x
# 6. CPython constant folding optimization
def optimized():
x = 2 * 3 * 4 # Compiled as x = 24 (computed at compile time)
return x
dis.dis(optimized)
# LOAD_CONST 1 (24) -- Not 2, 3, 4 separately!
- •CPython compiles .py to bytecode (.pyc), then the PVM interprets the bytecode
- •dis module disassembles bytecode for inspection
- •ast module shows the Abstract Syntax Tree before compilation
- •Bytecode is cached in __pycache__/ as .pyc files for faster loading
- •CPython performs optimizations like constant folding at compile time
- •Python is compiled (to bytecode), then interpreted; it is not purely interpreted
- •Bytecode is NOT machine code; it runs on the PVM, not directly on the CPU
- •.pyc files are implementation-specific; CPython's bytecodes differ from PyPy's