Introduction
A .pyc file holds a Python module compiled to bytecode, so that the next
import can skip compiling it. CPython 2.3 to 2.7
write one beside each .py file they import: foo.py gives
foo.pyc, or foo.pyo with the -O option.
The layout is an 8-byte header and then one code object in Python’s
marshal format. Every number in the code object is stored in 32 bits.
Bytecode changes between releases, so a file only runs on the release that wrote it,
and the magic number at the start says which that is.
A .pyc file dropped on any page of this site opens in the explorer,
which shows its structure, a disassembly and each code object.
See all the .pyc variations.
Structure
All numbers are little-endian. The header is 8 bytes and the rest of the file is one object, the module’s code object. Nothing follows it.
0magic: a 16-bit number, then0D 0A4 bytes4source_mtime: the modification time of the source file4 bytes8code object, in marshal formatvaries
The code object holds the module’s instructions, its constants and names, and a table of line numbers.
What each release changed
| Release | Magic number | Changes |
|---|---|---|
| 2.3 | 62011 | The numbers in a code object become 32-bit. Marshal adds True and False (T and F). SET_LINENO and FOR_LOOP are gone, so line numbers are only in co_lnotab. Opcode 86 is YIELD_VALUE |
| 2.4 | 62061 | Marshal writes interned strings with t and refers back to them with R. New instructions NOP and LIST_APPEND |
| 2.5 | 62131 | Marshal version 2: floats and complex numbers are binary (g, y), and sets are added (<, >). New instruction WITH_CLEANUP. New flags for absolute_import and with_statement |
| 2.6 | 62161 | New instruction STORE_MAP. New flags for print_function and unicode_literals |
| 2.7 | 62211 | LIST_APPEND moves and takes an argument. BUILD_MAP, LOAD_ATTR, COMPARE_OP, IMPORT_NAME and IMPORT_FROM move up by one number to make room for BUILD_SET. New conditional jumps POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE, JUMP_IF_FALSE_OR_POP and JUMP_IF_TRUE_OR_POP replace the two older ones. SETUP_WITH, SET_ADD and MAP_ADD are added, and EXTENDED_ARG moves |
Every pre-release number between these has its own entry on the Header page.
Telling it apart
The first two bytes, read as a little-endian number, are between 62011 and 62212, and
the next two are 0D 0A. The explorer goes by these numbers alone. The
usual Python 2.7 file starts 03 F3 0D 0A.
Files from Python 2.2 and earlier also start with a number above 10000, but their code
objects store numbers in 16 bits. Python 3 files have a much smaller magic number and
a co_kwonlyargcount field.
Variants
- Python 1.5 to 2.0 bytecode – 8-byte header and 16-bit numbers in the code object
- Python 2.1 and 2.2 bytecode – adds co_freevars and co_cellvars
- Python 2.3 to 2.7 bytecode – the numbers in a code object become 32-bit
- Python 3.0 to 3.2 bytecode – adds co_kwonlyargcount; names are Unicode
- Python 3.3 to 3.5 bytecode – 12-byte header, with the source size
- Python 3.6 bytecode – two-byte instructions
- Python 3.7 bytecode – 16-byte header, with flags and hash-based files
- Python 3.8 and 3.9 bytecode – adds co_posonlyargcount
- Python 3.10 bytecode – a line table replaces co_lnotab; jumps count instructions
- Python 3.11 and later bytecode – locals in one list, a location table, an exception table and inline caches
Links
Software
- CPython – the Python interpreter, which writes and loads these files; its
dismodule shows the bytecode - uncompyle6 – decompiler that turns these files back into Python source (any system with Python)
- Decompyle++ (pycdc) – decompiler and disassembler for .pyc files (Windows, macOS, Linux)
Developers
- marshal – Python’s own module for the object format (Python)
- py_compile – Python’s own module that writes .pyc files (Python)
- dis – Python’s own disassembler (Python)
- xdis – cross-version disassembler for .pyc files (Python)
Reference
- dis: Python bytecode instructions – every instruction, as documented for Python 2.7