One of the most important features of Mojo is its ability to work with Python.
Mojo is not designed to force developers to abandon the massive Python ecosystem. Instead, it provides interoperability that allows developers to combine Python’s libraries and productivity with Mojo’s compiled, performance-oriented programming model.
In simple terms, Mojo and Python can work in both directions:
Mojo → Python
Python → Mojo
This means you can:
- Import Python libraries inside Mojo
- Call Python functions from Mojo
- Use existing Python code in a Mojo application
- Pass values between Python and Mojo
- Expose Mojo functions to Python
- Move performance-critical Python code into Mojo
Current Mojo 1.0 documentation supports both interoperability directions, although calling Mojo from Python is still considered an early-development feature.
Why Does Mojo Work with Python?
Python has one of the largest programming ecosystems in the world.
Developers already rely on Python for:
- Artificial intelligence
- Machine learning
- Data science
- Scientific computing
- Automation
- Backend development
- Numerical computing
Requiring developers to rewrite all existing Python libraries in Mojo would make adoption extremely difficult.
Instead, Mojo allows developers to reuse Python code.
The idea is:
Existing Python Ecosystem
+
Mojo Performance
=
Combined Development Workflow
This makes it possible to gradually introduce Mojo into an existing Python project.
Two Ways Mojo Works with Python
There are two main interoperability directions.
1. Calling Python from Mojo
You write a Mojo program and use Python libraries inside it.
Mojo Application
↓
Python Library
↓
CPython
2. Calling Mojo from Python
You keep your Python application but move selected performance-sensitive functions into Mojo.
Python Application
↓
Mojo Module
↓
Compiled High-Performance Code
These two approaches solve different problems.
Calling Python From Mojo
Suppose you are writing a Mojo application but need a Python package.
You do not necessarily need to find or create a native Mojo replacement.
Mojo can load the Python module through CPython.
For example, the official documentation demonstrates importing NumPy into Mojo.
First import Mojo’s Python interoperability API:
from std.python import Python
Then:
def main() raises:
var np = Python.import_module("numpy")
var array = np.array(Python.list(1, 2, 3))
print(array)
Output:
[1 2 3]
Here Mojo is calling the real Python NumPy package through CPython.
Understanding Python.import_module()
The important line is:
var np = Python.import_module("numpy")
It is conceptually similar to Python:
import numpy as np
The difference is that Mojo needs its Python interoperability API.
The flow becomes:
Mojo
↓
Python.import_module()
↓
CPython
↓
NumPy
Python.import_module() returns the imported Python module wrapped in a PythonObject, allowing Mojo code to access its functions and objects.
What Is PythonObject?
PythonObject is one of the most important concepts when combining Mojo and Python.
Suppose Python creates:
numbers = [1, 2, 3]
When Mojo interacts with that Python object, Mojo needs a way to represent it.
That is where:
PythonObject
comes in.
Conceptually:
Python Object
↓
PythonObject Wrapper
↓
Mojo Code
The wrapper allows Mojo to interact with Python objects in a Python-like way.
Mojo’s documentation explains that wrapped Python objects expose common operations such as attribute and item access to the underlying Python object.
Mojo Types and Python Types
Mojo and Python have different type systems.
Mojo is statically typed, while Python is primarily dynamically typed.
For interoperability, values sometimes need to be converted.
For common primitive values, Mojo currently supports conversions involving types such as:
Mojo Int ↔ Python int
Mojo Float ↔ Python float
Mojo Bool ↔ Python bool
Mojo String ↔ Python str
Many common conversions happen automatically, although more complex objects can require explicit conversion.
Example of Mojo Values Passed to Python
Consider:
from std.python import Python
def main() raises:
var py_code = """
def show(value):
print(type(value))
"""
var module = Python.evaluate(
py_code,
file=True,
name="example"
)
module.show(10)
module.show(3.14)
module.show(True)
module.show("Mojo")
The Mojo values are converted into corresponding Python objects when passed into Python.
This interoperability helps make mixed Mojo/Python programs easier to write.
Using Existing Python Libraries in Mojo
One of the biggest benefits of Python interoperability is access to existing packages.
Imagine you need functionality that already exists in Python.
Instead of:
Need Feature
↓
No Native Mojo Library
↓
Rewrite Everything
you can potentially use:
Need Feature
↓
Existing Python Library
↓
Import From Mojo
This is particularly important while Mojo’s native ecosystem is still growing.
Installing Python Packages for Mojo
If your Mojo code imports:
numpy
then NumPy must actually exist in the Python environment used by your Mojo application.
For example, you might manage Mojo and Python dependencies using tools such as:
- Pixi
- uv
- Conda
The official Mojo documentation recommends using a package/environment manager to keep the required Python version and dependencies consistent.
Mojo Does Not Include Python
An important distinction is:
Mojo itself does not require Python.
You can write native Mojo programs without Python.
However, if you want to use Python interoperability, you need an appropriate CPython installation.
Current Mojo 1.0 documentation supports Python interoperability with:
Python 3.10 through Python 3.14.
So:
Normal Mojo Program
↓
Python Not Required
but:
Mojo + Python Libraries
↓
CPython Required
Mojo Uses the CPython Runtime
When Mojo calls Python code, it does not attempt to recreate Python.
It uses the standard CPython runtime.
This is important because it provides compatibility with existing Python packages.
Conceptually:
Mojo Program
↓
Python Interoperability Layer
↓
CPython Runtime
↓
Python Package
The official documentation says Python-from-Mojo interoperability uses CPython without modifying the Python runtime.
Using a Local Python File From Mojo
Mojo can also call your own Python code.
Suppose your project contains:
project/
│
├── main.mojo
└── calculations.py
Your Python file might contain:
def multiply(a, b):
return a * b
From Mojo, you can add the directory to Python’s module path:
from std.python import Python
def main() raises:
Python.add_to_path(".")
var calculations = Python.import_module("calculations")
print(calculations.multiply(10, 20))
Output:
200
The official documentation supports adding local directories with Python.add_to_path() before importing local Python modules.
Why main() raises Is Often Used
You may notice:
def main() raises:
instead of:
def main():
Python operations can raise exceptions.
For example:
Python.import_module("numpy")
could fail if NumPy is unavailable.
Therefore, functions interacting with Python frequently need to allow errors to propagate.
Python From Mojo Is Highly Compatible
Calling Python from Mojo is currently the more mature direction of interoperability.
Mojo uses the standard CPython runtime, allowing existing Python code to execute without being rewritten.
This makes workflows such as:
Mojo
↓
NumPy
or:
Mojo
↓
Existing Python Module
practical today.
Calling Mojo From Python
The opposite direction is especially interesting for existing Python applications.
Imagine you already have:
Large Python Application
but one calculation is slow.
You do not need to rewrite the entire project in Mojo.
Instead:
Python Application
↓
Normal Python Code
↓
Performance Bottleneck
↓
Mojo Function
↓
Compiled Execution
The Mojo documentation specifically presents Python-to-Mojo interoperability as a way to extend an existing Python project with high-performance Mojo code or incrementally migrate parts of it.
Basic Python + Mojo Project Structure
A simple project can look like:
project/
│
├── main.py
└── mojo_module.mojo
main.py contains your Python application.
mojo_module.mojo contains the Mojo functionality you want Python to call.
From Python, the goal is to eventually write something as natural as:
import mojo_module
print(mojo_module.factorial(5))
Output:
120
How Python Imports Mojo
Because Mojo is compiled, Python cannot simply interpret a .mojo source file as normal Python code.
Mojo therefore provides an import mechanism that compiles the Mojo source into a Python-compatible extension module.
A Python program can use:
import mojo.importer
import mojo_module
The importer finds the corresponding:
mojo_module.mojo
file and compiles it into a shared library that Python can load.
What Happens Behind the Scenes?
Suppose Python executes:
import mojo_module
With the Mojo importer active, the process is approximately:
Python
↓
import mojo_module
↓
Find mojo_module.mojo
↓
Compile Mojo
↓
Create Shared Library
↓
Load as Python Extension
↓
Call Mojo Functions
The generated shared library is cached so that it does not need to be rebuilt unnecessarily.
Current documentation describes a cache directory similar to:
__mojocache__/
containing the generated .so library.
Python Extension Modules
Python has long supported extension modules written in compiled languages.
For example, performance-critical Python functionality can be implemented using:
- C
- C++
- Rust
Mojo uses this same general Python extension mechanism.
Conceptually:
Python
↓
Extension Module
↓
Compiled Mojo
This is why Python can eventually treat exposed Mojo functionality similarly to an ordinary imported module.
Exposing Mojo Functions to Python
Not every Mojo function automatically becomes available to Python.
Mojo needs to know which functions and types should be exposed.
Current Mojo uses Python binding infrastructure including:
PythonModuleBuilder
The binding system creates the bridge between Python’s runtime and Mojo’s compiled functions.
The general idea is:
Mojo Function
↓
Python Binding
↓
Python Module
↓
Python Calls Function
Simple Factorial Example
Suppose a Mojo module contains a high-performance factorial function.
Python could eventually use it like:
import mojo.importer
import mojo_module
result = mojo_module.factorial(5)
print(result)
Output:
120
From the Python developer’s perspective, it behaves much like importing another module.
Behind the scenes, however, the implementation is compiled Mojo code.
Why Use Mojo From Python?
The main reason is:
Performance.
Suppose your application is:
90% Normal Python
10% Heavy Calculation
Rewriting the entire application might be unnecessary.
Instead:
90% Python
+
10% Mojo
can be a much more practical approach.
Example: Slow Python Loop
Imagine Python code processing millions of values:
def calculate():
total = 0
for i in range(10_000_000):
total += i * i
return total
If profiling shows that this calculation is a major bottleneck, one approach is to implement the computational portion in Mojo.
Your architecture could become:
Python
↓
Application Logic
↓
Mojo Function
↓
Heavy Computation
↓
Return Result to Python
The key point is not that every Python loop should automatically be rewritten. First identify an actual performance bottleneck.
Python + Mojo for AI
This combination becomes especially interesting in AI.
Python is excellent for:
- Model experimentation
- Data pipelines
- AI libraries
- Notebooks
- Application logic
Mojo focuses more heavily on:
- Performance
- CPU execution
- GPU execution
- Kernels
- Numerical computing
- Hardware optimization
So an AI system could conceptually use:
Python
↓
Model / Application Logic
↓
Mojo
↓
Performance-Critical Kernel
↓
CPU / GPU
Python + Mojo vs Python + C++
Many Python libraries currently use an architecture such as:
Python
↓
C / C++
↓
Hardware
Mojo creates the possibility of:
Python
↓
Mojo
↓
Hardware
This is one of Mojo’s most interesting long-term use cases.
Mojo provides Python-like syntax while using a compiled systems-programming model with static typing, ownership-aware semantics, and low-level control.
Python + Mojo vs Pure Mojo
You do not always need Python.
If everything you need exists natively in Mojo, you can write:
Pure Mojo Application
But if you depend heavily on Python packages:
Mojo
+
Python Libraries
may currently be more practical.
Likewise, existing Python projects can remain primarily Python and use Mojo only where necessary.
Performance Considerations
It is important to understand that:
Calling a Python library from Mojo does not magically make that Python code faster.
For example:
Mojo
↓
Python Function
still executes the Python function through CPython.
Mojo’s performance advantages come primarily when the performance-sensitive logic is actually implemented as native Mojo code.
Therefore:
Mojo → Python → Python Code
does not mean:
Python Code Automatically Becomes Mojo-Speed
This distinction is extremely important.
Example With NumPy
Suppose Mojo calls NumPy:
var np = Python.import_module("numpy")
The NumPy operations still use NumPy’s own implementations.
That can already be very fast because NumPy itself uses optimized native code internally.
The advantage here is ecosystem compatibility, not magically recompiling NumPy as Mojo.
Data Conversion Can Have a Cost
When Python and Mojo exchange data, values may need conversion or wrapping.
Conceptually:
Python Data
↓
Conversion / Wrapper
↓
Mojo Data
or:
Mojo Data
↓
Conversion
↓
Python Object
For small calls, this usually may not matter much.
But if millions of tiny calls constantly cross the Python/Mojo boundary, interoperability overhead can become important.
A better architecture is often:
Python
↓
Send Large Workload
↓
Mojo Processes Work
↓
Return Result
instead of crossing the boundary for every tiny operation.
PythonObject vs Native Mojo Types
There are two broad ways to deal with Python values in Mojo.
You can keep them as:
PythonObject
or convert them into native Mojo types.
For example:
Python int
↓
PythonObject
↓
Mojo Int
Native Mojo types are particularly useful when you want Mojo’s static typing and performance model.
Current Mojo provides conversion mechanisms between supported Python and Mojo values, but not every standard-library type has automatic conversion support yet.
Using PythonObject Directly
Sometimes you do not need to immediately convert everything.
You can work directly with Python objects.
This makes code more flexible and Python-like.
However, if your goal is maximum performance, converting data into appropriate native Mojo types and doing the heavy computation in Mojo is often more useful.
Do You Need to Rewrite Your Python Project?
Usually, no.
One of the strongest reasons for Python interoperability is incremental migration.
Instead of:
100,000 Lines of Python
↓
Rewrite Everything
↓
Mojo
you can potentially use:
100,000 Lines of Python
↓
Find Performance Bottleneck
↓
Rewrite Selected Function
↓
Mojo
That reduces migration cost and risk.
Example Migration Strategy
Suppose an application has:
API Layer
Database Logic
Authentication
Data Processing
Heavy Numerical Calculation
There may be little reason to rewrite authentication or ordinary database code in Mojo.
Instead:
Python:
✓ API
✓ Database
✓ Authentication
✓ Application Logic
Mojo:
✓ Heavy Numerical Calculation
This is a more realistic use of the two languages.
Current Limitations of Calling Mojo From Python
There is one important warning:
Calling Mojo from Python is currently a beta/early-development feature.
The official Mojo 1.0 documentation says developers should expect changes to the API and ergonomics.
Current documented limitations include areas such as:
- Limits around bound function arguments
- Keyword-argument support
- Mojo package dependencies
- Properties
- Some type conversions
These limitations can change as interoperability matures.
Calling Python From Mojo vs Calling Mojo From Python
| Feature | Python From Mojo | Mojo From Python |
|---|---|---|
| Main Application | Mojo | Python |
| Purpose | Reuse Python ecosystem | Add Mojo performance |
| Python Libraries | Directly usable | Existing app stays Python |
| Mojo Performance Code | Main application can use it | Selected modules/functions |
| Maturity | More established | Still early/beta |
| Best For | Mojo apps needing Python packages | Existing Python projects |
When Should You Call Python From Mojo?
Use this approach when:
- Your main application is Mojo
- You need an existing Python library
- Mojo does not yet have the required package
- You are migrating Python code gradually
- You need to reuse existing Python modules
Example:
Mojo Application
+
Existing Python Library
When Should You Call Mojo From Python?
Use this approach when:
- You already have a Python project
- Profiling identifies a performance bottleneck
- You need faster custom computation
- You want to experiment with Mojo gradually
- You do not want to rewrite your entire Python application
Example:
Existing Python Application
+
Performance-Critical Mojo Module
Recommended Python-to-Mojo Workflow
A sensible migration workflow is:
Build Application in Python
↓
Measure Performance
↓
Find Actual Bottleneck
↓
Evaluate Existing Optimized Libraries
↓
Move Suitable Code to Mojo
↓
Create Python Binding
↓
Benchmark Again
The measure first step is important.
Do not rewrite working Python code just because Mojo exists.
Do Python Developers Need to Learn New Concepts?
Yes.
Mojo may look like Python, but it is not simply faster Python.
Python developers moving into native Mojo should learn concepts such as:
- Static typing
- Value semantics
- Ownership
- Explicit mutability
- Memory management
- Structs
- Compile-time programming
- Parallelism
Mojo’s official guide for Python developers specifically emphasizes that its execution and ownership model is closer to systems languages such as Rust, Swift, and C++ than to ordinary Python.
Is Mojo Compatible With Every Python Library?
Calling Python from Mojo uses CPython, which gives Mojo broad compatibility with the Python ecosystem.
However, that does not mean every possible integration pattern will always work perfectly.
Some libraries may depend on:
- Special runtime behavior
- Native extensions
- Environment configuration
- Complex object conversions
- Framework-specific behavior
So real projects should test the specific libraries they depend on.
Does mojo build Include Python Packages?
No.
This is an important deployment detail.
If your Mojo application uses a Python package such as NumPy, running:
mojo build app.mojo
does not bundle NumPy and the Python runtime into the resulting application automatically.
The required Python interpreter and packages still need to exist in the runtime environment.
So deployment can look like:
Compiled Mojo Application
+
Compatible Python
+
Required Python Packages
Best Practices for Mojo and Python
When combining the two languages:
- Keep performance-critical work in Mojo where it provides a measurable benefit.
- Reuse mature Python libraries instead of rewriting them unnecessarily.
- Minimize unnecessary crossings between Python and Mojo.
- Convert values carefully when native Mojo types are required.
- Keep Python and Mojo dependencies reproducible.
- Benchmark before and after moving code.
- Remember that calling Python from Mojo does not automatically accelerate Python code.
- Treat Python-to-Mojo bindings as evolving technology for now.
Example Real-World Architecture
A future AI or numerical application might look like:
Python Application
↓
Data Processing
↓
AI Framework
↓
Custom Mojo Module
↓
Performance-Critical Computation
↓
CPU / GPU
Meanwhile, a Mojo-first application could look like:
Mojo Application
↓
Native Mojo Logic
↓
Need Existing Library
↓
Python Interoperability
↓
Python Package
Both approaches are valid.
Frequently Asked Questions
Can Mojo run Python code?
Yes. Mojo can import Python modules and call Python APIs using the CPython runtime.
Can Mojo import NumPy?
Yes. NumPy is used as an example in the official Mojo documentation:
from std.python import Python
def main() raises:
var np = Python.import_module("numpy")
The NumPy package must be installed in the Python environment.
Can Python call Mojo functions?
Yes. Mojo provides bindings that allow selected Mojo functions and types to be exposed as Python extension modules. This feature is still in early development.
Does Mojo make Python code faster automatically?
No. Calling Python code from Mojo still runs that code through CPython. For Mojo’s native performance benefits, performance-critical logic generally needs to execute as Mojo code.
What is PythonObject in Mojo?
PythonObject is Mojo’s wrapper representation for interacting with Python objects. It allows Mojo to access and manipulate objects originating from Python.
Does Mojo need Python installed?
Not for normal native Mojo programs. Python is required when using Mojo’s Python interoperability features. Current Mojo 1.0 documentation supports Python 3.10–3.14 for interoperability.
Should I rewrite my entire Python project in Mojo?
Usually not. A more practical approach is to profile your Python project, identify genuine performance bottlenecks, and evaluate whether selected components benefit from Mojo.
Can I use my own Python files from Mojo?
Yes. You can add a local directory using Python.add_to_path() and then import the module with Python.import_module().
Is Python-to-Mojo interoperability production ready?
It exists in Mojo 1.0, but the official documentation currently labels calling Mojo from Python as a beta/early-development feature and warns that its APIs and ergonomics may change.
Final Thoughts
Mojo’s Python interoperability is important because developers do not have to make a strict choice between:
Python OR Mojo
Instead, they can increasingly use:
Python + Mojo
There are two main approaches:
Mojo → Python
Use this when your main program is Mojo but you want access to Python libraries.
And:
Python → Mojo
Use this when your existing Python application needs selected high-performance Mojo code.
A practical architecture can therefore be:
Python
↓
Application Logic
Libraries
AI Ecosystem
↓
Mojo
↓
Performance-Critical Code
CPU / GPU Work
The key idea is simple: Python provides an enormous mature ecosystem, while Mojo provides a modern compiled programming model for performance-sensitive work.
Instead of immediately replacing Python, Mojo can work alongside it—allowing developers to preserve existing Python code and libraries while gradually introducing Mojo where its performance and hardware-control capabilities are most useful.




