Python跨语言调用其他语言方法
- 通过FFI(外部程序接口)调用,Java中称JNI,将Rust/Go/C/C++按C类型编译称动态连接库,Python中使用ctypes加载并调用
- 通过进程间通信IPC调用(如使用socket)
- 通过网络接口调用,如RPC,HTTP接口等
- 通过本地虚拟机/解释器/代理调用,如Python调用Java, 执行JavaScript等
- 通过WASM接口调用
Rust编写C动态连接库
Rust按C类型构建为动态连接库,Python中使用ctypes,加载动态连接库并调用
- 使用cargo新建rust库
cargo new calc --lib
- 打开calc项目,修改Cargo.toml,增加lib选项
Cargo.toml
...
[lib]
crate-type = ["dylib"]
- 在src/lib.rs中编写被调用函数
src/lib.rs
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
#[no_mangle]
pub extern "C" fn add_int(a: i32, b: i32) -> i32 {
a + b
}
#[no_mangle]
pub extern "C" fn add_float(a: f32, b: f32) -> f32 {
a + b
}
#[no_mangle]
pub extern "C" fn add_str<'a>(a: *const c_char, b: *const c_char) -> *mut c_char {
let a_cstr = unsafe {CStr::from_ptr(a)};
let a_str = a_cstr.to_str().unwrap();
let b_cstr = unsafe {CStr::from_ptr(b)};
let b_str = b_cstr.to_str().unwrap();
let c_string = String::from(a_str) + &String::from(b_str);
let c = CString::new(c_string).unwrap().into_raw();
c
}
其中
-
#[no_mangle]
表示禁止Rust编译器修改原函数名称 -
pūb extern "C"
指将函数导出为C类型外部可调用函数 - 由于Rust中的char和C中的不太一样,变换方法为
*c_char
->CStr
->&str
->String
->CString
->*c_char
- 编译为动态链接库
在项目根目录执行
cargo build --release
会在项目target/release目录,根据不同的操作系统生成 libcalc.dylib 或 libcalc.dylib 或 libcalc.dll动态链接库
- 使用Python调用动态链接库方法
call_rust.py
import ctypes
lib = ctypes.CDLL("./target/release/libcalc.dylib")
print(lib.add_int(3, 5))
lib.add_float.argtypes = [ctypes.c_float, ctypes.c_float]
lib.add_float.restype = ctypes.c_float
print(lib.add_float(3.2, 5.3))
lib.add_str.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
lib.add_str.restype = ctypes.c_char_p
print(lib.add_str(b'Hello', b'World'))
运行输出如下:
8
8.5
b'HelloWorld'
参考