2 min read

Python Bytecode

Python is an interpreted language, but it also involves a compilation step. Python source code (.py) is first compiled into bytecode, which is a low-level, platform-independent set of instructions. This bytecode is then executed by the Python Virtual Machine (PVM).

The Compilation Process

  1. Source Code: You write Python code in .py files.
  2. Bytecode: When you run the program, Python compiles the source code into bytecode. This bytecode is often cached in .pyc files inside the __pycache__ directory to speed up subsequent loads.
  3. PVM: The Python Virtual Machine reads the bytecode and executes the corresponding machine code instructions on your computer's CPU.

Viewing Bytecode

You can inspect the bytecode of Python functions or modules using the built-in dis module (Disassembler).

import dis

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

dis.dis(add)

Output Example:

  4           0 LOAD_FAST                0 (a)
              2 LOAD_FAST                1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE

For more details on the dis module, see programming/python/dis-module.

Understanding Instructions

Each line in the disassembly output represents a bytecode instruction:

  • Line Number: The line number in the original source code.
  • Address: The byte offset of the instruction in the bytecode sequence.
  • Opcode: The name of the operation (e.g., LOAD_FAST, BINARY_ADD).
  • Argument: The argument for the operation, if any.

Code Objects

Bytecode is stored inside code objects. Every function in Python has a __code__ attribute that holds its code object.

def my_func():
    pass

print(my_func.__code__)
# Output: <code object my_func at ...>

print(my_func.__code__.co_code)
# Output: b'd\x00S\x00' (The raw bytecode bytes)

programming/python/python