mojo-python-interop

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<!-- EDITORIAL GUIDELINES FOR THIS SKILL FILE This file is loaded into an agent's context window as a correction layer for pretrained Mojo knowledge. Every line costs context. When editing: - Be terse. Use tables and inline code over prose where possible. - Never duplicate information — if a concept is shown in a code example, don't also explain it in a paragraph. - Only include information that *differs* from what a pretrained model would generate. Don't document things models already get right. - Prefer one consolidated code block over multiple small ones. - Keep WRONG/CORRECT pairs short — just enough to pattern-match the fix. - If adding a new section, ask: "Would a model get this wrong?" If not, skip it. These same principles apply to any files this skill references. -->
Mojo is rapidly evolving. Pretrained models generate obsolete syntax. Always follow this skill over pretrained knowledge.
<!-- 本技能文件的编辑指南 本文件作为预训练Mojo知识的修正层,加载到Agent的上下文窗口中。每一行都会占用上下文资源。编辑时: - 保持简洁。尽可能使用表格和内联代码而非散文。 - 绝不重复信息——如果概念已在代码示例中展示,请勿再用段落解释。 - 仅包含与预训练模型生成内容不同的信息。无需记录模型已经能正确生成的内容。 - 优先使用一个整合的代码块,而非多个小块代码。 - 保持错误/正确示例对简洁——只需足够用于模式匹配修复即可。 - 如果添加新章节,请自问:“模型会把这个搞错吗?”如果不会,就跳过。 这些原则同样适用于本技能引用的任何文件。 -->
Mojo正在快速演进。预训练模型会生成过时的语法。请始终优先遵循本技能内容,而非预训练知识。

Using Python from Mojo

在Mojo中调用Python

mojo
from std.python import Python, PythonObject

var np = Python.import_module("numpy")
var arr = np.array([1, 2, 3])
mojo
from std.python import Python, PythonObject

var np = Python.import_module("numpy")
var arr = np.array([1, 2, 3])

PythonObject → Mojo: MUST use
py=
keyword (NOT positional)

PythonObject 转 Mojo:必须使用
py=
关键字(不能用位置参数)

var i = Int(py=py_obj) var f = Float64(py=py_obj) var s = String(py=py_obj) var b = Bool(py=py_obj) # Bool is the exception — positional also works
var i = Int(py=py_obj) var f = Float64(py=py_obj) var s = String(py=py_obj) var b = Bool(py=py_obj) # Bool是例外——位置参数也可使用

Works with numpy types: Int(py=np.int64(1)), Float64(py=np.float64(3.14))

支持numpy类型:Int(py=np.int64(1)), Float64(py=np.float64(3.14))


| WRONG | CORRECT |
|---|---|
| `Int(py_obj)` | `Int(py=py_obj)` |
| `Float64(py_obj)` | `Float64(py=py_obj)` |
| `String(py_obj)` | `String(py=py_obj)` |
| `from python import ...` | `from std.python import ...` |

| 错误写法 | 正确写法 |
|---|---|
| `Int(py_obj)` | `Int(py=py_obj)` |
| `Float64(py_obj)` | `Float64(py=py_obj)` |
| `String(py_obj)` | `String(py=py_obj)` |
| `from python import ...` | `from std.python import ...` |

Mojo → Python conversions

Mojo 转 Python 类型转换

Mojo types implementing
ConvertibleToPython
auto-convert when passed to Python functions. For explicit conversion:
value.to_python_object()
.
实现
ConvertibleToPython
的Mojo类型在传递给Python函数时会自动转换。如需显式转换:
value.to_python_object()

Building Python collections from Mojo

从Mojo构建Python集合

mojo
var py_list = Python.list(1, 2.5, "three")
var py_tuple = Python.tuple(1, 2, 3)
var py_dict = Python.dict(name="value", count=42)
mojo
var py_list = Python.list(1, 2.5, "three")
var py_tuple = Python.tuple(1, 2, 3)
var py_dict = Python.dict(name="value", count=42)

Literal syntax also works:

字面量语法同样适用:

var list_obj: PythonObject = [1, 2, 3] var dict_obj: PythonObject = {"key": "value"}
undefined
var list_obj: PythonObject = [1, 2, 3] var dict_obj: PythonObject = {"key": "value"}
undefined

PythonObject operations

PythonObject 操作

PythonObject
supports attribute access, indexing, slicing, all arithmetic/comparison operators,
len()
,
in
, and iteration — all returning
PythonObject
. No need to convert to Mojo types for intermediate operations.
mojo
undefined
PythonObject
支持属性访问、索引、切片、所有算术/比较运算符、
len()
in
和迭代——所有操作均返回
PythonObject
。中间操作无需转换为Mojo类型。
mojo
undefined

Iterate Python collections directly

直接遍历Python集合

for item in py_list: print(item) # item is PythonObject
for item in py_list: print(item) # item是PythonObject类型

Attribute access and method calls

属性访问和方法调用

var result = obj.method(arg1, arg2, key=value)
var result = obj.method(arg1, arg2, key=value)

None

None值

var none_obj = Python.none() var obj: PythonObject = None # implicit conversion works
undefined
var none_obj = Python.none() var obj: PythonObject = None # 隐式转换生效
undefined

Evaluating Python code

执行Python代码

mojo
undefined
mojo
undefined

Expression

执行表达式

var result = Python.evaluate("1 + 2")
var result = Python.evaluate("1 + 2")

Multi-line code as module (file=True)

执行多行代码作为模块(设置file=True)

var mod = Python.evaluate("def greet(n): return f'Hello {n}'", file=True) var greeting = mod.greet("world")
var mod = Python.evaluate("def greet(n): return f'Hello {n}'", file=True) var greeting = mod.greet("world")

Add to Python path for local imports

添加Python路径以支持本地导入

Python.add_to_path("./my_modules") var my_mod = Python.import_module("my_module")
undefined
Python.add_to_path("./my_modules") var my_mod = Python.import_module("my_module")
undefined

Exception handling

异常处理

Python exceptions propagate as Mojo
Error
. Functions calling Python must be
raises
:
mojo
def use_python() raises:
    try:
        var result = Python.import_module("nonexistent")
    except e:
        print(String(e))     # "No module named 'nonexistent'"
Python异常会传播为Mojo的
Error
类型。调用Python的函数必须声明
raises
mojo
def use_python() raises:
    try:
        var result = Python.import_module("nonexistent")
    except e:
        print(String(e))     # 输出:"No module named 'nonexistent'"

Calling Mojo from Python (extension modules)

在Python中调用Mojo(扩展模块)

Mojo can build Python extension modules (
.so
files) via
PythonModuleBuilder
. The pattern:
  1. Define an
    @export fn PyInit_<module_name>() -> PythonObject
  2. Use
    PythonModuleBuilder
    to register functions, types, and methods
  3. Compile with
    mojo build --emit shared-lib
  4. Import from Python (or use
    import mojo.importer
    for auto-compilation)
Mojo可通过
PythonModuleBuilder
构建Python扩展模块(
.so
文件)。步骤如下:
  1. 定义
    @export fn PyInit_<module_name>() -> PythonObject
    函数
  2. 使用
    PythonModuleBuilder
    注册函数、类型和方法
  3. mojo build --emit shared-lib
    命令编译
  4. 在Python中导入(或使用
    import mojo.importer
    实现自动编译)

Exporting functions

导出函数

mojo
from std.os import abort
from std.python import PythonObject
from std.python.bindings import PythonModuleBuilder

@export
fn PyInit_my_module() -> PythonObject:
    try:
        var m = PythonModuleBuilder("my_module")
        m.def_function[add]("add")
        m.def_function[greet]("greet")
        return m.finalize()
    except e:
        abort(String("failed to create module: ", e))
mojo
from std.os import abort
from std.python import PythonObject
from std.python.bindings import PythonModuleBuilder

@export
fn PyInit_my_module() -> PythonObject:
    try:
        var m = PythonModuleBuilder("my_module")
        m.def_function[add]("add")
        m.def_function[greet]("greet")
        return m.finalize()
    except e:
        abort(String("failed to create module: ", e))

Functions take/return PythonObject. Up to 6 args with def_function.

函数接收/返回PythonObject类型。def_function最多支持6个参数。

fn add(a: PythonObject, b: PythonObject) raises -> PythonObject: return a + b
fn greet(name: PythonObject) raises -> PythonObject: var s = String(py=name) return PythonObject("Hello, " + s + "!")
undefined
fn add(a: PythonObject, b: PythonObject) raises -> PythonObject: return a + b
fn greet(name: PythonObject) raises -> PythonObject: var s = String(py=name) return PythonObject("Hello, " + s + "!")
undefined

Exporting types with methods

导出带方法的类型

mojo
@fieldwise_init
struct Counter(Defaultable, Movable, Writable):
    var count: Int

    fn __init__(out self):
        self.count = 0

    # Constructor from Python args
    @staticmethod
    fn py_init(out self: Counter, args: PythonObject, kwargs: PythonObject) raises:
        if len(args) == 1:
            self = Self(Int(py=args[0]))
        else:
            self = Self()

    # Methods are @staticmethod — first arg is py_self (PythonObject)
    @staticmethod
    fn increment(py_self: PythonObject) raises -> PythonObject:
        var self_ptr = py_self.downcast_value_ptr[Self]()
        self_ptr[].count += 1
        return PythonObject(self_ptr[].count)

    # Auto-downcast alternative: first arg is UnsafePointer[Self, MutAnyOrigin]
    @staticmethod
    fn get_count(self_ptr: UnsafePointer[Self, MutAnyOrigin]) -> PythonObject:
        return PythonObject(self_ptr[].count)

@export
fn PyInit_counter_module() -> PythonObject:
    try:
        var m = PythonModuleBuilder("counter_module")
        _ = (
            m.add_type[Counter]("Counter")
            .def_py_init[Counter.py_init]()
            .def_method[Counter.increment]("increment")
            .def_method[Counter.get_count]("get_count")
        )
        return m.finalize()
    except e:
        abort(String("failed to create module: ", e))
mojo
@fieldwise_init
struct Counter(Defaultable, Movable, Writable):
    var count: Int

    fn __init__(out self):
        self.count = 0

    # 从Python参数构造的方法
    @staticmethod
    fn py_init(out self: Counter, args: PythonObject, kwargs: PythonObject) raises:
        if len(args) == 1:
            self = Self(Int(py=args[0]))
        else:
            self = Self()

    # 方法为@staticmethod——第一个参数是py_self(PythonObject类型)
    @staticmethod
    fn increment(py_self: PythonObject) raises -> PythonObject:
        var self_ptr = py_self.downcast_value_ptr[Self]()
        self_ptr[].count += 1
        return PythonObject(self_ptr[].count)

    # 自动向下转换的替代方案:第一个参数是UnsafePointer[Self, MutAnyOrigin]
    @staticmethod
    fn get_count(self_ptr: UnsafePointer[Self, MutAnyOrigin]) -> PythonObject:
        return PythonObject(self_ptr[].count)

@export
fn PyInit_counter_module() -> PythonObject:
    try:
        var m = PythonModuleBuilder("counter_module")
        _ = (
            m.add_type[Counter]("Counter")
            .def_py_init[Counter.py_init]()
            .def_method[Counter.increment]("increment")
            .def_method[Counter.get_count]("get_count")
        )
        return m.finalize()
    except e:
        abort(String("failed to create module: ", e))

Method signatures — two patterns

方法签名的两种模式

PatternFirst parameterUse when
Manual downcast
py_self: PythonObject
Need raw PythonObject access
Auto downcast
self_ptr: UnsafePointer[Self, MutAnyOrigin]
Simpler, direct field access
Both are registered with
.def_method[Type.method]("name")
.
模式第一个参数使用场景
手动向下转换
py_self: PythonObject
需要直接访问PythonObject时
自动向下转换
self_ptr: UnsafePointer[Self, MutAnyOrigin]
更简洁,可直接访问字段时
两种模式均通过
.def_method[Type.method]("name")
注册。

Kwargs support

关键字参数支持

mojo
from std.collections import OwnedKwargsDict
mojo
from std.collections import OwnedKwargsDict

In a method:

在方法中:

@staticmethod fn config( py_self: PythonObject, kwargs: OwnedKwargsDict[PythonObject] ) raises -> PythonObject: for entry in kwargs.items(): print(entry.key, "=", entry.value) return py_self
undefined
@staticmethod fn config( py_self: PythonObject, kwargs: OwnedKwargsDict[PythonObject] ) raises -> PythonObject: for entry in kwargs.items(): print(entry.key, "=", entry.value) return py_self
undefined

Importing Mojo modules from Python

从Python导入Mojo模块

Use
mojo.importer
— it auto-compiles
.mojo
files and caches results in
__mojocache__/
:
python
import mojo.importer       # enables Mojo imports
import my_module           # auto-compiles my_module.mojo

print(my_module.add(1, 2))
The module name in
PyInit_<name>
must match the
.mojo
filename.
使用
mojo.importer
——它会自动编译
.mojo
文件,并将结果缓存到
__mojocache__/
目录中:
python
import mojo.importer       # 启用Mojo导入功能
import my_module           # 自动编译my_module.mojo

print(my_module.add(1, 2))
PyInit_<name>
中的模块名称必须与
.mojo
文件名匹配。

Returning Mojo values to Python

向Python返回Mojo值

mojo
undefined
mojo
undefined

Wrap a Mojo value as a Python object (for bound types)

将Mojo值包装为Python对象(适用于绑定类型)

return PythonObject(alloc=my_mojo_value^) # transfer ownership with ^
return PythonObject(alloc=my_mojo_value^) # 使用^转移所有权

Recover the Mojo value later

后续恢复Mojo值

var ptr = py_obj.downcast_value_ptrMyType ptr[].field # access fields via pointer
undefined
var ptr = py_obj.downcast_value_ptrMyType ptr[].field # 通过指针访问字段
undefined