Skip to main content
Log in

Python interoperability

Because Mojo uses a Pythonic syntax, its easy to start reading and writing Mojo when coming from Python. Mojo also optimizes for ease of use in across the Python-Mojo language boundary, with built-in support for both calling into Python from Mojo, and calling into Mojo from Python.

The common API for interoperability in both directions is the PythonObject type, which wraps a Python object within Mojo.

In Mojo, you can import Python modules, construct Python objects, and call Python functions and methods directly. Mojo will first load the CPython interpreter as a dynamic library (called libpython.dylib on macOS), and use that interpreter to execute Python code. For example:

๐Ÿ”ฅ Mojo
from python import Python

fn main():
# Loads CPython dynamically behind the scenes; returns a PythonObject
var res = Python.evaluate("2 + 2")
from python import Python

fn main():
# Loads CPython dynamically behind the scenes; returns a PythonObject
var res = Python.evaluate("2 + 2")

Calling into Mojo from Python is different. Because Mojo is a compiled language, we can't directly "evaluate" Mojo code. Instead, Mojo code must declare up front which functions and types are available to be called from Python. For example:

๐Ÿ”ฅ mojo_module.mojo
@export
fn PyInit_mojo_module() -> PythonObject:
try:
var m = PythonModuleBuilder("mojo_module")
m.def_function[mojo_greet]("mojo_greet", docstring="Say hello from Mojo")
return m.finalize()
except e:
return abort[PythonObject](String("error creating Python Mojo module:", e))

fn mojo_greet(name: PythonObject):
print("Hello to", name, "from Mojo ๐Ÿ‘‹")
@export
fn PyInit_mojo_module() -> PythonObject:
try:
var m = PythonModuleBuilder("mojo_module")
m.def_function[mojo_greet]("mojo_greet", docstring="Say hello from Mojo")
return m.finalize()
except e:
return abort[PythonObject](String("error creating Python Mojo module:", e))

fn mojo_greet(name: PythonObject):
print("Hello to", name, "from Mojo ๐Ÿ‘‹")

By defining a suitable PyInit_*() function, Mojo performs the necessary low-level binding calls to inform Python how to call Mojo code:

๐Ÿ main.py
import max._mojo.mojo_importer
import mojo_module

mojo_module.mojo_greet("Python")
import max._mojo.mojo_importer
import mojo_module

mojo_module.mojo_greet("Python")

(Although it's not quite that simple yet.)

These quick examples give you a taste of what interoperability looks like for Python and Mojo. Flexible interop enables you to move incrementally and efficiently. By embracing both directions of language interop, you can choose how to use Mojo in a way that works best for your use case.

To learn more about bridging Python โ†” Mojo, continue reading:

Was this page helpful?