社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

怎么给 Python 写 C 扩展?

编程派 • 8 年前 • 744 次点击  

作者:nucpylab

原文:http://www.cnblogs.com/nucpylab/p/8608722.html

1. 环境准备

如果是Linux只需要安装Python3.x + Python-dev。

Windows下稍微复杂点,VS2017 + Python3.6.3

VS2017可用社区版,需要选择安装的环境如下:

2. Hello World !

2.1 C模块封装

以计算两个数相加为例,选择任意文件夹,新建如下C语言源码:

  1. // 文件名 calc.c

  2. #include

  3. int add(int x, int y){ // C 函数

  4.    return x + y;

  5. }

  6. static PyObject *calc_add(PyObject *self, PyObject *args){

  7.    int x, y;

  8.    // Python传入参数

  9.    // "ii" 表示传入参数为2个int型参数,将其解析到x, y变量中

  10.    if(!PyArg_ParseTuple(args, "ii", &x, &y))

  11.        return NULL;

  12.    return PyLong_FromLong(add(x, y));

  13. }

  14. // 模块的方法列表

  15. static PyMethodDef CalcMethods[] = {

  16.     {"add", calc_add, METH_VARARGS, "函数描述"},

  17.     {NULL, NULL, 0, NULL}

  18. };

  19. // 模块

  20. static struct PyModuleDef calcmodule = {

  21.    PyModuleDef_HEAD_INIT,

  22.    "calc", // 模块名

  23.    NULL, // 模块文档

  24.    -1,       /* size of per-interpreter state of the module,

  25.                or -1 if the module keeps state in global variables. */

  26.    CalcMethods

  27. };

  28. // 初始化

  29. PyMODINIT_FUNC PyInit_calc(void)

  30. {

  31.    return PyModule_Create(&calcmodule);

  32. }

其中,静态函数 calc_add 以python的C接口方式封装了add函数,命名方式 模块名_函数名

静态PyMethodDef列表 变量 CalcMethods 包含了该模块方法的描述

静态struct PyModuleDef结构体  变量 calcmodule 定义了模块的描述

PyInit_calc 函数初始化了模块,命名方式 PyInit_模块名

2.2 C源码编译

在VS2017中可以直接生成 .dll 文件,然后改名为 .pyd 就可在python程序中引入该模块了,但是,这不"清真",正确的姿势是写一个 setup.py然后通过python调cl.exe编译。

新建 setup.py文件,内容如下:

  1. # setup.py

  2. from distutils.core import setup, Extension

  3. module1 = Extension('calc',

  4.                    sources=['calc.c'])

  5. setup(name='calc_model',

  6.      version='1.0',

  7.      description='Hello ?',

  8.      ext_modules=[module1]

  9. )

然后,从Windows的命令行(命令提示符)下进入到这个文件夹下,执行:

python setup.py build

即可完成编译,如果出现某 .bat文件未找到,说明你的VS没有安装相应的依赖(Linux下编译不成功原因可能是没有装python-dev),按文章开头给出的依赖库添加修改(此时不需要重新安装VS)。

编译结束后,在该文件夹下会出现 build 文件夹,进入该文件夹,出现如下两个文件夹:

进入 lib.xxx那个文件夹,里面有个 .pyd 结尾的文件(Linux下为 .so 结尾),这就是我们编译好的python模块了,如下:

当然,你也可以改名为 calc.pyd 比较好看,不过这不影响调用。

2.3 Python调用

这部分就简单了,进入含有编译好的 .pyd 文件夹,新建如下文件:

  1. import calc

  2. print(calc.add(12, 21))

这就是一个普通库,这样调用就OK了。

3. Python的参数传递以及C的返回值相关问题

这部分我直接甩出文件就行,编译及调用过程与上面一样。

C 文件

  1. /**构建返回值

  2. Py_BuildValue("")                        None

  3. Py_BuildValue("i", 123)                  123

  4. Py_BuildValue("iii", 123, 456, 789)      (123, 456, 789)

  5. Py_BuildValue("s", "hello")              'hello'

  6. Py_BuildValue("y", "hello")              b'hello'

  7. Py_BuildValue("ss", "hello", "world")    ('hello', 'world')

  8. Py_BuildValue("s#", "hello", 4)          'hell'

  9. Py_BuildValue("y#", "hello", 4)          b'hell'

  10. Py_BuildValue("()")                      ()

  11. Py_BuildValue("(i)", 123)                (123,)

  12. Py_BuildValue("(ii)", 123, 456)          (123, 456)

  13. Py_BuildValue("(i,i)", 123, 456)         (123, 456)

  14. Py_BuildValue("[i,i]", 123, 456)         [123, 456]

  15. Py_BuildValue("{s:i,s:i}", "abc", 123, "def", 456)    {'abc': 123, 'def': 456}

  16. Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6)          (((1, 2), (3, 4)), (5, 6))

  17. **/

  18. #include

  19. static PyObject *value_commonArgs(PyObject *self, PyObject *args){

  20.    // 传入普通参数,例如: s = value.com(1, 2.3, "Hello C")

  21.    int x;

  22.    double y;

  23.    char *z;

  24.    if(!PyArg_ParseTuple(args , "ids", &x, &y, &z))

  25.        return NULL;

  26.    printf("The args is %d and %f and %s .n", x, y, z);

  27.    // 返回(x, y, z)的元组

  28.    return Py_BuildValue("(i,d,s)",x, y, z);

  29. }

  30. static PyObject *value_tupleTest(PyObject *self, PyObject *args){

  31.    // t = value.tut((1, 3), "Tuple")

  32.    int x, y;

  33.    char *z ;

  34.    if(!PyArg_ParseTuple(args, "(ii)s", &x, &y, &z))

  35.        return NULL;

  36.    printf("The args is (%d, %d), %s .n", x, y, z);

  37.    // return ([1, 2], "hello")

  38.    return Py_BuildValue("[i,i]s", x, y, z);

  39. }

  40. static PyObject *value_some(PyObject *self, PyObject *args){

  41.    /* 可选参数,可能是下面几种, "|" 代表后面的参数可选

  42.        c = value.som(1)

  43.        value.som(1, 3)

  44.        value.som(1, 2, "hello")

  45.    */

  46.    int x = 0, y = 0;

  47.    char *z = NULL;

  48.    if(!PyArg_ParseTuple(args, "i|is", &x, &y, &z))

  49.        return NULL;

  50.    printf("x is: %dn", x);

  51.    printf("y is: %dn", y);

  52.    if(z != NULL)printf("z is: %sn", z);

  53.    return


题图:pexels,CC0 授权。

点击阅读原文,查看更多 Python 教程和资源。


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/dcWl7lT4op
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/9182