You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过PyO3实现Rust向Python传递回调函数

实现Rust通过PyO3向Python传递可多次调用的回调函数

核心思路

PyO3通过wrap_pyfunction!宏可以将Rust函数包装为Python可调用对象,Python侧能在任意时机多次调用该对象,Rust函数会在每次调用时处理传入的数据。关键是保证回调函数执行时持有Python GIL(PyO3的包装宏会自动处理这一点)。

完整代码示例

1. Python侧代码(test.py)

class Test:
    def test_fn(self, callback):
        # 模拟不同业务时机调用回调
        callback("初始化完成,第一次回调")
        print("Python执行中间逻辑:处理数据")
        callback("数据处理完成,第二次回调")
        print("Python执行收尾逻辑:资源清理")
        callback("任务结束,第三次回调")

2. Rust侧配置与代码

首先在Cargo.toml中添加PyO3依赖:

[package]
name = "rust_py_callback"
version = "0.1.0"
edition = "2021"

[dependencies]
pyo3 = { version = "0.20", features = ["extension-module"] }
方案一:作为Python扩展模块(src/lib.rs)
use pyo3::prelude::*;
use pyo3::types::PyModule;

// Rust回调函数:接收Python传入的字符串,处理后返回结果
fn my_callback(py: Python, msg: &str) -> PyResult<()> {
    println!("[Rust回调] 收到消息:{}", msg);
    // 这里可以添加任意Rust逻辑,比如修改本地状态、调用其他Rust函数等
    Ok(())
}

// 将Rust函数包装为Python可调用对象
fn wrap_rust_callback(py: Python) -> PyResult<PyObject> {
    let py_callback = wrap_pyfunction!(my_callback, py)?;
    Ok(py_callback.into())
}

// 定义Python可调用的扩展函数,用于触发整个流程
#[pyfunction]
fn run_python_test(py: Python) -> PyResult<()> {
    // 加载本地的test.py模块
    let test_module = PyModule::import(py, "test")?;
    // 获取Test类并创建实例
    let test_class = test_module.getattr("Test")?;
    let test_instance = test_class.call0()?;
    // 生成Python可调用的回调对象
    let callback = wrap_rust_callback(py)?;
    // 调用Python类的方法,传入回调
    test_instance.call_method1("test_fn", (callback,))?;
    Ok(())
}

// 定义Python扩展模块
#[pymodule]
fn rust_py_callback(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(run_python_test, m)?)?;
    Ok(())
}
方案二:Rust直接启动Python解释器(src/main.rs)

如果不需要做Python扩展,而是让Rust程序直接加载Python逻辑:

use pyo3::prelude::*;
use pyo3::types::PyModule;

fn my_callback(py: Python, msg: &str) -> PyResult<()> {
    println!("[Rust回调] 收到消息:{}", msg);
    Ok(())
}

fn wrap_rust_callback(py: Python) -> PyResult<PyObject> {
    let py_callback = wrap_pyfunction!(my_callback, py)?;
    Ok(py_callback.into())
}

fn main() -> PyResult<()> {
    // 初始化Python解释器并持有GIL
    Python::with_gil(|py| {
        let test_module = PyModule::import(py, "test")?;
        let test_class = test_module.getattr("Test")?;
        let test_instance = test_class.call0()?;
        let callback = wrap_rust_callback(py)?;
        test_instance.call_method1("test_fn", (callback,))?;
        Ok(())
    })
}

编译与运行(不使用maturin)

Linux/macOS环境

  1. 编译Rust代码:
cargo build --release
  1. 若使用扩展模块方案,重命名动态库为Python可识别的名称:
cp target/release/librust_py_callback.so rust_py_callback.so
  1. 运行Python脚本(扩展模块方案):
# main.py
import rust_py_callback
rust_py_callback.run_python_test()
python main.py
  1. 若使用Rust直接启动方案,直接运行:
cargo run --release

Windows环境

  1. 编译Rust代码:
cargo build --release
  1. 若使用扩展模块方案,重命名动态库:
copy target\release\rust_py_callback.dll rust_py_callback.pyd
  1. 后续运行步骤与Linux/macOS一致。

关键注意事项

  • GIL管理:wrap_pyfunction!宏自动处理GIL的获取与释放;如果自定义PyClass作为回调,需在方法中通过Python::with_gil或参数中的Python实例确保GIL持有。
  • 状态共享:如果需要在回调中修改Rust的全局/共享状态,单线程场景用RefCell,多线程场景用Mutex,但多线程需注意GIL限制(同一时间仅一个线程能持有GIL)。
  • 参数类型:示例使用字符串,PyO3支持自动转换大多数基础类型(i32、f64、bool等),自定义类型需实现PyFromPyObject或IntoPy<PyObject> trait。

后续建议

  • 虽然当前未使用maturin,但它能自动处理跨平台动态库命名、Python环境适配,简化扩展模块发布流程,后续可尝试引入。
  • 若需异步回调场景(比如Python异步任务调用Rust函数),可结合pyo3-asyncio和async-trait实现异步回调逻辑。

内容的提问来源于stack exchange,提问作者V.Rashkov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 04:20:56