Introduction
When CPython imports a module from a
.py file, it compiles the source to bytecode
and saves the result as a .pyc file in a __pycache__ folder
beside the source. The next import loads the .pyc file and skips the
compiler. The file holds the module’s code object: its instructions, constants and
names, with the code object of every function and class nested inside.
This page covers the format written by Python 3.12. Its code object keeps local, cell and free variables in one list, with a qualified name, a location table that records lines and columns, and an exception table. Instructions are two bytes, and some are followed by inline caches.
The magic number at the start of the file says which release wrote it, and a file only runs on a release with exactly the same number. The final Python 3.12 releases write 3531.
A .pyc file dropped on any page of this site opens in the explorer, which
shows its structure, a disassembly and each code object.
Structure
A file is a 16-byte header followed by one object in Python’s
marshal format, the module’s code object. All numbers are little-endian.
0magic: 3531, then0D 0A4 bytes4flags: 0 for a date check, 1 or 3 for a hash check4 bytes8source_mtime and source_size, or source_hash8 bytes16code object of the module, onemarshalobjectvaries
The code object holds counts, flags, the instructions as a byte string, a tuple of constants (which holds nested code objects), and tables of names, source positions and exception handlers.
What Python 3.12 changed
The header and the fields of the code object are those of Python 3.11. The changes are in the instructions and in what the compiler puts in the code object:
- List, dictionary and set comprehensions are compiled inline, in the code object
that contains them, instead of as a nested function (PEP 709). Their variables can
be marked hidden in
co_localspluskinds, a new bit, 0x10. PRECALLis gone.LOAD_METHODis merged intoLOAD_ATTR, whose argument’s low bit now asks for a method.- Conditional jumps only go forward:
POP_JUMP_IF_FALSE,POP_JUMP_IF_TRUE,POP_JUMP_IF_NONEandPOP_JUMP_IF_NOT_NONEreplace the forward and backward forms, andJUMP_IF_FALSE_OR_POPandJUMP_IF_TRUE_OR_POPare gone. COMPARE_OPkeeps its operator above bit 4 of the argument.CALL_INTRINSIC_1andCALL_INTRINSIC_2take over rare operations that had their own instructions: printing in the REPL,import *, unary+, and others.- New instructions include
RETURN_CONST,LOAD_FAST_CHECK,LOAD_FAST_AND_CLEAR,LOAD_SUPER_ATTR,BINARY_SLICE,STORE_SLICE,END_FOR,END_SENDandCLEANUP_THROW, and, for the type parameter syntax of PEP 695,LOAD_LOCALS,LOAD_FROM_DICT_OR_GLOBALSandLOAD_FROM_DICT_OR_DEREF. FOR_ITERandSENDgain an inline cache, and the caches ofLOAD_ATTR,LOAD_GLOBAL,CALL,COMPARE_OPandBINARY_SUBSCRchange size.YIELD_VALUEtakes an argument. The compiler puts anEND_FORat the end of eachforloop.- The location table merges neighbouring instructions that share a location into one entry.
Every magic number of the 3.12 pre-releases is on the header page.
Telling it apart
The first two bytes, little-endian, are the magic number: 3500 to 3531, and 3531 in
every final 3.12 release. The next two bytes are 0D 0A. The explorer takes
these numbers and then reads the file strictly: only 0, 1 and 3 are valid flags, and the
code object must fill the rest of the file exactly.
Files of Python 3.11 and 3.13 have the same layout, but magic numbers of 3450 to 3495 and 3550 to 3571, and number their instructions differently.
00000000 CB 0D 0D 0A 00 00 00 00 00 F1 53 65 06 00 00 00
Variants
- Python 1.5 to 2.0 bytecode – 8-byte header, one or three-byte instructions and 16-bit numbers in the code object
- Python 2.1 and 2.2 bytecode – adds free and cell variables to the code object
- Python 2.3 to 2.7 bytecode – the code object’s numbers become 32-bit
- Python 3.0 to 3.2 bytecode – keyword-only arguments; text and bytes become separate types
- Python 3.3 to 3.5 bytecode – 12-byte header with the source size; object references from 3.4
- Python 3.6 bytecode – two-byte instructions
- Python 3.7 bytecode – 16-byte header with a flags field
- Python 3.8 and 3.9 bytecode – positional-only arguments
- Python 3.10 bytecode –
co_linetable, and jumps that count instructions - Python 3.11 bytecode – one list of locals, a location table, an exception table and inline caches
- Python 3.12 bytecode – inlined comprehensions, method loads folded into
LOAD_ATTR, forward-only conditional jumps - Python 3.13 bytecode – instructions renumbered, explicit conversion to
bool, caches on jumps - Python 3.14 bytecode –
marshalversion 5 with slice constants, small integers in the argument - Python 3.15 bytecode –
marshalversion 6, lazy imports and a cache onRESUME
Links
Software
- CPython – the Python interpreter, which writes and loads these files;
python -m disshows the bytecode (Windows, macOS, Linux) - Decompyle++ (pycdc) – disassembler and decompiler for
.pycfiles of many Python versions (Windows, macOS, Linux)
Developers
- dis – Python’s own bytecode disassembler (Python)
- marshal – Python’s own module for the object format of the body (Python)
- py_compile – Python’s own module that writes
.pycfiles (Python) - importlib – Python’s import system, which reads and checks them (Python)
- xdis – cross-version disassembler (Python)
Reference
- dis – Python’s documentation of its bytecode instructions (Python 3.12)
- marshal – Python’s documentation of the module and its version numbers (Python 3.12)
- importlib – Python’s documentation of the import system (Python 3.12)
- PEP 3147 – the
__pycache__folder and its file names - PEP 552 – the flags field and hash-based
.pycfiles - PEP 626 – precise line numbers for tracing
- PEP 657 – columns in the location table
- PEP 709 – inlined comprehensions
- locations.md – CPython’s description of the location table (Python 3.12)
- exception_handling_notes.txt – CPython’s description of the exception table (Python 3.12)
- _bootstrap_external.py – the importer, whose comments list every magic number (Python 3.12)