Python Mypyc Compilation — Core Concepts
What mypyc is
Mypyc is a compiler that transforms type-annotated Python modules into C extension modules. It is built on top of mypy (the static type checker) and uses the same type analysis infrastructure. The more precise your type annotations, the faster the compiled code runs.
The output is a standard .so (Linux/macOS) or .pyd (Windows) file that Python imports like any other C extension. Your module’s public API stays identical — callers cannot tell it was compiled.
How it works
# fib.py — standard typed Python
def fib(n: int) -> int:
if n <= 1:
return n
a, b = 0, 1
for _ in range(n - 1):
a, b = b, a + b
return b
Compile it:
# Install mypyc (ships with mypy)
pip install mypy
# Compile the module
mypyc fib.py
# Produces: fib.cpython-311-x86_64-linux-gnu.so
Now import fib loads the compiled version automatically. The function fib(40) runs 3-5x faster because mypyc generates C code that uses native long arithmetic instead of Python object operations.
Why type hints matter for speed
In standard Python, every operation goes through a generic dispatch:
# Python: a + b
1. What type is a? (check at runtime)
2. What type is b? (check at runtime)
3. Does a have __add__? (lookup at runtime)
4. Call a.__add__(b) (dispatch at runtime)
5. Handle TypeError if incompatible (check at runtime)
With type annotations, mypyc knows the types at compile time and generates direct C operations:
# Mypyc compiled: a + b (both int)
1. Add two C longs (single CPU instruction)
2. Check for overflow (branch prediction handles this)
This is where the speedup comes from — eliminating the type-checking and dispatch overhead that dominates tight loops and computation-heavy code.
What compiles well
Mypyc excels with specific code patterns:
| Pattern | Speedup | Why |
|---|---|---|
| Integer/float arithmetic loops | 3-5x | Native C arithmetic |
| Class-heavy code with typed attributes | 2-4x | Direct struct access vs dict lookup |
| String operations | 1.5-3x | Avoids PyObject overhead |
| Function calls between compiled modules | 2-4x | Direct C function calls |
Code using list[int], dict[str, int] | 2-3x | Specialized container operations |
Code that does not benefit much:
- I/O-bound operations (networking, file reads)
- Code calling untyped libraries extensively
- Heavily dynamic code (eval, exec, monkey-patching)
Compiling a package
For multi-file packages, use setup.py or pyproject.toml:
# setup.py
from setuptools import setup
from mypyc.build import mypycify
setup(
name="mypackage",
ext_modules=mypycify([
"mypackage/__init__.py",
"mypackage/core.py",
"mypackage/utils.py",
]),
)
pip install -e .
Only compile the performance-critical modules. Modules with heavy dynamic behavior can remain as regular Python.
Limitations to know
Mypyc restricts some Python dynamism in compiled modules:
- No monkey-patching compiled classes or functions from outside.
- No dynamic attribute addition to instances of compiled classes (they use fixed slots).
- Limited metaclass support — custom metaclasses may not work.
- No
*args/**kwargsin some contexts — typed signatures are preferred.
These restrictions exist because mypyc generates fixed C structs for classes and direct function calls. The dynamic features Python allows would break these assumptions.
Mypyc vs Cython
| Aspect | Mypyc | Cython |
|---|---|---|
| Input language | Standard Python + type hints | Cython language (Python superset) |
| Learning curve | Low (just add types) | Medium (new syntax for C types) |
| Maximum speedup | 2-5x typical | 10-100x with full C typing |
| Build integration | pip/setuptools | pip/setuptools |
| Type system | Python’s typing module | Own type system + Python types |
Mypyc is the better choice when you want moderate speedups with minimal code changes. Cython is better when you need maximum performance and are willing to write more specialized code.
Common misconception
“Mypyc is just mypy with extra steps — type checking already makes code faster.”
Type checking with mypy does not affect runtime performance at all. Mypy only analyzes your code statically and reports errors. Mypyc takes the extra step of actually using that type information to generate compiled C code. Same type annotations, completely different outcome — one catches bugs, the other makes code faster.
One thing to remember: Mypyc rewards you for good typing practices — the type annotations you already write for code quality become performance optimizations when compiled, giving you 2-5x speedups on CPU-bound Python.
See Also
- Python Appimage Distribution An AppImage is like a portable app on a USB stick — download one file, double-click it, and your Python program runs on any Linux computer without installing anything.
- Python Briefcase Native Apps Imagine a travel agent who repacks your suitcase for each country's customs — Briefcase converts your Python app into proper native packages for every platform.
- Python Flatpak Packaging Flatpak wraps your Python app in a safe bubble that works on every Linux system — like a snow globe that keeps your program perfect inside.
- Python Nuitka Compilation What if your Python code could run as fast as a race car instead of a bicycle? Nuitka translates Python into C to make that happen.
- Python Pex Executables Imagine zipping your entire Python project into a single magic file that runs anywhere Python lives — that's what PEX does.