Interop
Mojo can import and use Python modules directly. This lets you leverage the entire Python ecosystem — NumPy, Pandas, matplotlib — while writing performance-critical code in Mojo. The boundary between Python and Mojo has a cost, and understanding that cost is essential.
Code
from std.python import Python, PythonObject
fn main() raises:
var np = Python.import_module("numpy")
var plt = Python.import_module("matplotlib.pyplot")
var py_list: PythonObject = [1, 2, 3, 4, 5]
var arr = np.array(py_list)
print(arr)
print(np.sum(arr)) # 15
var x: PythonObject = [1, 2, 3]
var y: PythonObject = [1, 4, 9]
plt.plot(x, y)
plt.show()
To run this, you need to add both numpy and matplotlib to your Mojo environment.
Add the following content to your pixi.toml file.
[dependencies]
numpy = "*"
matplotlib = "*"
Then run pixi install
Object Bridging
Python objects in Mojo are wrapped as PythonObject. Every operation on a PythonObject goes
through the Python runtime — dynamic dispatch, reference counting, GIL acquisition:
Constraint
Import NumPy, create a 1000-element array, and sum it using both np.sum() and a manual Mojo loop over the elements. Compare the approaches — the manual loop through PythonObject will be dramatically slower than np.sum().
Why It Matters
Every Python interop call crosses a boundary: Mojo → CPython C API → Python runtime → GIL → dynamic dispatch. For bulk operations, use Python libraries (they're already optimized in C). For tight loops, stay in pure Mojo. Never cross the boundary inside a hot loop.