Python 互连¶
概述¶
通过 extern "python" 调用 Python 标准库或第三方库。Link 会动态加载 libpython,通过 CPython C API 调用 Python 函数。
工作原理
Link 内部维护一个 PythonRuntime,首次使用 extern "python" 时自动初始化:
- 加载 Python 共享库(
python3.dll/libpython3.so) - 调用
Py_Initialize()初始化解释器 - 通过
PyImport_ImportModule导入指定模块 - 通过
PyObject_GetAttrString获取函数 - 用
PyObject_CallFunction调用,自动转换 Link 与 Python 类型
基本用法¶
调用 math 模块¶
extern "python" module "math" {
fn sqrt(x: f64) -> f64;
fn pow(base: f64, exp: f64) -> f64;
fn log(x: f64) -> f64;
fn log2(x: f64) -> f64;
fn floor(x: f64) -> f64;
fn ceil(x: f64) -> f64;
}
println("sqrt(16.0) =", sqrt(16.0)); // 4
println("pow(2, 10) =", pow(2.0, 10.0)); // 1024
println("log(e) =", log(2.71828)); // 1.0
println("floor(3.7) =", floor(3.7)); // 3
调用 os 模块¶
extern "python" module "os" {
fn getcwd() -> str;
fn getpid() -> i32;
}
println("当前目录:", getcwd());
println("进程 ID:", getpid());
调用 json 模块¶
extern "python" module "json" {
fn dumps(obj: str) -> str;
}
// 注意:v0.1 暂不支持直接传递 dict,只能传字符串
let s = dumps("{\"name\": \"Link\", \"version\": 0.1}");
println(s);
类型映射¶
| Link 类型 | Python 类型 | 转换方式 |
|---|---|---|
i32 / i64 |
int |
PyLong_FromLongLong |
f32 / f64 |
float |
PyFloat_FromDouble |
bool |
bool |
PyBool_FromLong |
str |
str |
PyUnicode_DecodeUTF8 |
none |
None |
Py_BuildValue("z", NULL) |
list |
list |
(规划中) |
支持的函数签名¶
Python FFI 支持 0~2 个参数,返回类型可以是基本类型:
extern "python" module "math" {
fn pi() -> f64; // 0 参数
fn sqrt(x: f64) -> f64; // 1 参数
fn pow(base: f64, exp: f64) -> f64; // 2 参数
}
extern "python" module "os" {
fn getcwd() -> str;
fn getpid() -> i32;
}
找到 Python 库¶
Link 会自动按以下顺序查找 Python 共享库:
Windows¶
python3.dll(Python 3.x 主 DLL,通常在 Python 安装目录)python311.dll/python310.dll/python39.dll(具体版本)- PATH 中的所有
python3*.dll
Linux¶
libpython3.solibpython3.11.so.1.0/libpython3.10.so.1.0(具体版本)libpython3.so(系统中python3 -c "import sys; print(sys.version)")
macOS¶
- Python Framework 内嵌的 libpython
- Homebrew 安装的
libpython3.dylib
手动指定¶
如果自动查找失败,可以手动指定 DLL 路径(规划中):
完整示例¶
数学计算¶
extern "python" module "math" {
fn sqrt(x: f64) -> f64;
fn pow(base: f64, exp: f64) -> f64;
fn sin(x: f64) -> f64;
fn cos(x: f64) -> f64;
fn radians(deg: f64) -> f64;
}
fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
let dx = x2 - x1;
let dy = y2 - y1;
sqrt(pow(dx, 2.0) + pow(dy, 2.0))
}
let d = distance(0.0, 0.0, 3.0, 4.0);
println("距离 =", d); // 5
let r = radians(90.0);
let s = sin(r);
println("sin(90°) =", s); // 1.0
系统信息¶
extern "python" module "os" {
fn getcwd() -> str;
fn getpid() -> i32;
fn uname() -> str; // 注意:实际返回 tuple,这里简化
}
extern "python" module "time" {
fn time() -> f64;
}
println("当前目录:", getcwd());
println("进程 ID:", getpid());
println("时间戳:", time());
混合使用多种语言¶
// C 标准库
extern "C" {
fn abs(n: i32) -> i32;
}
// Python 标准库
extern "python" module "math" {
fn sqrt(x: f64) -> f64;
fn pow(base: f64, exp: f64) -> f64;
}
// C++ 共享库
extern "C++" module "examples/cpp_demo.dll" {
fn cpp_factorial(n: i32) -> i32;
}
// 在一个函数中混合调用三种语言
fn complex_calc(x: f64) -> f64 {
let a = abs(-3); // C
let b = sqrt(x); // Python
let c = cpp_factorial(3); // C++
b * a + c // 4 * 3 + 6 = 18 (x=16)
}
println("结果:", complex_calc(16.0));
性能注意事项¶
Python 调用开销
每次 Python 函数调用有约 1-5 微秒开销(类型转换 + GIL 获取)。对于热路径代码:
- 简单数学运算优先用 C / C++ FFI
- Python 适合调用其丰富的标准库(如
json/re/datetime) - 批量处理优于循环单次调用
故障排查¶
"Failed to load Python library"¶
Python 未安装或未在 PATH 中。解决方法:
- Windows:确认安装时勾选 "Add Python to PATH"
- Linux:安装
python3-dev(Ubuntu)或python3-devel(CentOS) - macOS:
brew install python
"Symbol 'Py_Initialize' not found"¶
加载到了错误的 Python 库(如 embedded Python)。确保 python3.dll 是完整版而非 stub。
Python 异常¶
Python 函数抛出异常时,Link 会捕获并转为错误信息:
检查输入参数是否合法。
"Failed to import module 'xxx'"¶
Python 模块不存在或不在 sys.path 中。检查:
- 模块名拼写
- 是否安装了第三方库(
pip install xxx) - 当前 Python 环境是否正确(虚拟环境可能未激活)