(P1) Python Binding Other Language
动机、参考资料、涉及内容
动机
- pytorch 中的 C++ 代码是怎么跟 python 代码交互的,怎么用源码安装pytorch(研究清楚可能困难比较大)
- SPTAG 与 faiss 这两个项目是怎么用 swig 来得到 python 接口的
- cython, ctypes 等模块的使用
- Hanlp 1.7.x 版本 java 代码是怎么与python进行绑定的
原生方式
此方法为原生支持
一个示例
计算素数的个数,使用 C 实现,并将其封装为一个 Python 包来进行调用。
素材完全来自博客
c_extension/
fastcount.c
setup.py
test.py
fastcount.c
文件内容如下:
#include <Python.h>
// #include <stdio.h>
int c_prime_counter(int frm, int til) {
// 有些奇怪: 如果不加上 \n, 后续调用就不会打印
// printf("from: %d, to %d\n", frm, til);
int primecount = 0;
for (int num = frm; num <= til; num++) {
int flag = 0;
if (num > 1) {
for (int candidate = 2; candidate < num; candidate++) {
if ((num % candidate) == 0) {
flag = 1;
break;
}
}
if (flag == 0) {
primecount++;
}
}
}
return primecount;
}
static PyObject *py_primecounter(PyObject *self, PyObject *args) {
// Declare two pointers
int *n_frm, *n_til = NULL;
// Parse arguments - expects two integers that will be mapped to n_frm and n_til
// 注意此处传参为指针的指针, 为什么? 感觉跟 CUDA 的 cudaMalloc 函数有点像
if (!PyArg_ParseTuple(args, "ii", &n_frm, &n_til)) {
return NULL;
}
// Call c-function
// 这里的调用有些奇怪, 为什么不是传 *n_frm 与 *n_til
int found_primes = c_prime_counter(n_frm, n_til);
return PyLong_FromLong(found_primes);
}
static PyMethodDef CountingMethods[] = {
{"primecounter", py_primecounter, METH_VARARGS, "Function for counting primes in a range in c"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef fastcountmodule = {
PyModuleDef_HEAD_INIT,
"Fastcount", // module name
"C library for counting fast",
-1,
CountingMethods
};
PyMODINIT_FUNC PyInit_Fastcount(void) {
return PyModule_Create(&fastcountmodule);
};
setup.py
文件内容如下:
from distutils.core import setup, Extension
def main():
setup(
name="Fastcount",
version="1.0.0",
description="Fastcount module in python",
author="Mike",
author_email="mikehuls42@gmail.com",
ext_modules=[Extension("Fastcount", ["fastcount.c"])]
)
if (__name__ == "__main__"):
main()
test.py
文件内容如下:
import Fastcount
import time
def py_prime_counter(frm, til):
primecount = 0
for num in range(frm, til+1):
flag = 0
if num > 1:
for candidate in range(2, num):
if (num % candidate) == 0:
flag = 1
break
if flag == 0:
primecount += 1
return primecount
a, b = 1, 20000
t1 = time.time()
result = Fastcount.primecounter(a, b)
t2 = time.time()
print(f"[c] result:{result}, time: {t2 - t1:.2f}")
t1 = time.time()
result = py_prime_counter(a, b)
t2 = time.time()
print(f"[python] result:{result}, time: {t2 - t1:.2f}")
使用方式如下:
cd c_extension
python setup.py install
cd ..
python test.py
ctypes
待完善与合并
Cython
略
pybind11
一个最简的例子
来源于官方文档
目录结构
example.cpp
test.py
example.cpp
文件内容
#include <pybind11/pybind11.h>
namespace py = pybind11;
int add(int i, int j) {
return i + j;
}
// PYBIND11_MODULE 是一个宏, 注意这里的 example 应与文件名对应, 参考:
// https://stackoverflow.com/questions/49111874/creating-a-python-package-for-c-code-wrapped-with-pybind11-using-setuptools
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function that adds two numbers",
py::arg("i"), py::arg("j"));
}
test.py
文件内容
# 编译为(适配python导入的)动态链接库后,可以直接import
import example
print(example.add(1, j=2))
运行方式为
# 编译得到类似 example.cpython-39.x86_64-linux-gnu.so 的动态链接库文件
c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)
python test.py