What Is a .PYC File?

Python compiled bytecode

📂Binary
🏷️.pyc
🎯application/octet-stream

Python Compiled Bytecode (.pyc, .pyo)

Overview

A .pyc file contains compiled Python bytecode: the intermediate instructions the CPython virtual machine actually executes. When you import a module, Python compiles the .py source to bytecode and caches the result so that subsequent imports skip the compilation step.

These files appear automatically in __pycache__ directories. They are a cache, not a build artifact you are meant to manage - deleting them is always safe, and Python regenerates them on the next import.

An important misconception to clear up: .pyc files are not obfuscated or protected. Bytecode is straightforwardly decompilable back to readable source, so shipping .pyc instead of .py provides essentially no intellectual property protection.

Technical Specifications

Format Details

  • MIME Type: application/octet-stream
  • File Extensions: .pyc, .pyo (removed in Python 3.5), .pyd (Windows extension module, actually a DLL)
  • Category: Binary
  • Location: __pycache__/module.cpython-312.pyc
  • Contains: a header plus a marshalled code object

Magic numbers

The first four bytes identify the bytecode version. They change with essentially every Python release, because the instruction set changes:

Python 3.8:   55 0D 0D 0A
Python 3.9:   61 0D 0D 0A
Python 3.10:  6F 0D 0D 0A
Python 3.11:  A7 0D 0D 0A
Python 3.12:  CB 0D 0D 0A

The 0D 0D 0A suffix (\r\r\n) is deliberate: it corrupts if the file is transferred in text mode, causing an immediate, obvious failure rather than a subtle one.

This version-specific magic is why a .pyc compiled by Python 3.11 is simply rejected by 3.12 - Python detects the mismatch and recompiles from source.

File Structure

module.cpython-312.pyc
├── Bytes 0–3     Magic number (interpreter version)
├── Bytes 4–7     Bit field (flags; determines the next section's meaning)
├── Bytes 8–15    Either:
│                   • timestamp (mtime) + source size    [timestamp-based]
│                   • source hash                        [hash-based, PEP 552]
└── Bytes 16+     Marshalled code object

Invalidation modes

  • Timestamp-based (default) - stores the source file's modification time and size. If either differs, the cache is stale.
  • Hash-based (PEP 552, Python 3.7+) - stores a hash of the source instead, which makes builds reproducible and removes dependence on filesystem timestamps. Useful for deterministic packaging.

The code object

The bulk of the file is a marshalled code object containing the bytecode instructions, constants, names, argument counts, and nested code objects for each function and class defined in the module.

Bytecode in Practice

Inspecting bytecode is straightforward with the standard library:

import dis

def add_and_double(a, b):
    return (a + b) * 2

dis.dis(add_and_double)
  2           RESUME                   0
  3           LOAD_FAST                a
              LOAD_FAST                b
              BINARY_OP                0 (+)
              LOAD_CONST               1 (2)
              BINARY_OP                5 (*)
              RETURN_VALUE

Reading a cached file directly:

import dis, marshal, importlib.util, sys

with open("__pycache__/mymodule.cpython-312.pyc", "rb") as f:
    magic = f.read(4)
    assert magic == importlib.util.MAGIC_NUMBER, "compiled by a different Python"
    f.read(12)                      # flags + timestamp/hash + size
    code = marshal.load(f)

dis.dis(code)

History and Development

CPython has cached bytecode since its earliest versions, originally writing module.pyc alongside module.py. That caused problems: stale .pyc files left behind after deleting a .py would continue to import silently, and files compiled by different interpreter versions collided.

PEP 3147 (Python 3.2) introduced the __pycache__ directory with version-tagged filenames, so multiple Python versions can coexist and orphaned caches no longer shadow deleted source.

.pyo files held optimised bytecode produced with the -O flag. Because the optimisation level was not recorded in the filename, the same problem recurred. PEP 488 (Python 3.5) removed .pyo entirely, replacing it with an optimisation tag in the name: module.cpython-312.opt-1.pyc.

PEP 552 (Python 3.7) added hash-based invalidation for reproducible builds.

Common Use Cases

  • Import caching: the everyday purpose; faster startup on repeated imports.
  • Deployment without sources: shipping only bytecode, though see the caveats below.
  • Container image optimisation: pre-compiling with compileall so containers do not compile on first run.
  • Read-only filesystems: pre-compiling ahead of deployment where the runtime cannot write caches.
  • Malware analysis: reverse-engineering Python-based samples distributed as bytecode.
  • Forensics: recovering source from .pyc files when the originals are gone.

How to Open a PYC File

Disassembling with the standard library

# Disassemble a compiled file
python -m dis __pycache__/mymodule.cpython-312.pyc

# Compile a source file explicitly
python -m py_compile mymodule.py

# Pre-compile an entire tree
python -m compileall -q ./src

# Hash-based, deterministic output
python -m compileall --invalidation-mode checked-hash ./src

Decompiling back to source

Several tools reconstruct readable Python from bytecode:

  • decompyle3 and uncompyle6: mature decompilers, strongest for Python 3.8 and earlier.
  • pycdc (Decompyle++) - a C++ decompiler with broader version coverage.
# Decompile a single file
uncompyle6 __pycache__/mymodule.cpython-38.pyc > recovered.py

# pycdc
pycdc mymodule.cpython-312.pyc > recovered.py

Decompiler support consistently lags new Python releases, since each version changes the instruction set. For very recent versions, disassembly with dis may be the only option.

Recovering the Python version from an unknown file

xxd -l 4 unknown.pyc
# Compare the first 4 bytes against the magic number table
import importlib.util
print(importlib.util.MAGIC_NUMBER)   # this interpreter's magic

Security and Distribution Considerations

  • Bytecode is not protection. Decompilers recover near-original source, including names and structure. If you need to protect logic, the answer is licensing, server-side execution, or a compiled extension - not .pyc.
  • A .pyc is executable code. Python will import and run one without the corresponding source present, so a planted .pyc in a package directory executes on import. Treat unexpected cache files as suspicious.
  • Version pinning is implicit. Distributing bytecode ties your package to one exact interpreter version.
  • Do not commit __pycache__: add it to .gitignore; the files are machine- and version-specific noise.

Advantages

  • Faster imports: skips parsing and compilation on every run.
  • Automatic: requires no configuration or build step.
  • Version-safe: the magic number prevents mismatched bytecode from loading.
  • Coexistence: __pycache__ tagging lets several Python versions share a source tree.
  • Reproducible option: hash-based invalidation supports deterministic builds.

Limitations

  • Not portable across versions: every Python release invalidates existing caches.
  • No real speed gain at runtime: only import time improves; execution speed is unchanged.
  • No obfuscation value: trivially decompiled.
  • CPython-specific: PyPy, Jython, and IronPython use different mechanisms.
  • Cache staleness edge cases: timestamp-based invalidation can be fooled by filesystems with coarse timestamps or by restored backups.
  • PYTHON: the source these files are compiled from.
  • JAVABYTECODE: the JVM equivalent, with a comparable decompilation story.
  • WASM: another portable bytecode target.
  • PICKLE: Python's other binary format, using the same marshal-adjacent serialisation ideas but for data.

File Information

File Description

Python compiled bytecode

Category

Binary

Extensions

.pyc, .pyo

MIME Type

application/octet-stream

Related File Types

Other file types in the Binary category you might also need:

Start Analyzing PYTHONBYTECODE Files Now

Use our free AI-powered tool to detect and analyze Python compiled bytecode files instantly with Google's Magika technology.

Try File Detection Tool