用Cython把Python模块转换为纯C语言模块

转换为C语言模块后可以与Python解释器编译到一起作为内置模块用,也可以外部模块import后使用。

1. 创建嵌入式模块

嵌入式模块可以嵌入到独立C语言程序中执行
首先编写普通的python模块代码,然后在python代码的基础上创建pyx代码,通过cdef声明可以定义原生C语言函数,而不用通过Python解释器调用,可以更好的与C语言交互。
pyx代码的写法可以参考cython官方文档
hello.pyx文件

cdef public char *say_hello_to(char* name):
    return name

c-test.c文件

#include "Python.h"
#include "hello.h"
int main(int argc, char *argv[])
{
    PyObject *pmodule;
    char *result;
    wchar_t *program;

   // 读取命令行参数
    program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0], got %d arguments\n", argc);
        exit(1);
    }
    /* 把模块注册为内置模块 */

    if (PyImport_AppendInittab("test", PyInit_hello) == -1) {

        fprintf(stderr, "Error: could not extend in-built modules table\n");

        exit(1);

    }
    /* 把命令行参数传给Python解释器 */
    Py_SetProgramName(program);

    /* 初始化Python,这一步是必须的. */
    Py_Initialize();

    /* 调用Python中的函数. */
    result = say_hello_to("hetao");
    // PyErr_Print();
    printf("result:%s\n", result);

      /* Clean up after using CPython. */
    PyMem_RawFree(program);
    Py_FinalizeEx();

    return 0;

    /* Clean up in the error cases above. */
exit_with_error:
    PyMem_RawFree(program);
    Py_Finalize();
    return 1;
}

cython hello.pyx --3str
执行cython后会生成hello.c和hello.h两个文件

gcc -o libhello.so -shared  -fPIC -I/usr/include/python3.10 hello.c
gcc -o test-c -I/usr/include/python3.10 -I./ -L./ -ltest -lpython3.10 -Wl,-rpath . test-c.c

运行结果

$ ./test-c
result:hetao

2. 创建外部模块

hello.pyx文件

def say_hello_to(name):
    return name

setup.py文件

from setuptools import setup
from Cython.Build import cythonize

setup(
    name='Hello',
    ext_modules=cythonize("hello.pyx"),
)

构建模块
python setup.py build_ext --inplace

test-hello.py文件

import hello

result = hello.say_hello_to("hetao")
print(result)

cdef、def 和 cpdef 的区别:

在 Cython 中,有三种不同的函数声明方式:cdef、def 和 cpdef。
cdef 声明的函数是纯 C 函数,只能从 Cython 代码中调用,不可从 Python 代码中访问。
def 声明的函数是 Python 函数,可以从 Python 代码中调用,但会带来一些性能开销。
cpdef 声明的函数是混合函数,既可以从 Cython 代码中调用,也可以从 Python 代码中调用,其底层其实生成了两个函数版本。

关于编译参数:

获取编译参数
python3-config –cflags
获取链接参数
python3-config –ldflags

发表回复