• 『Python CoolBook』C扩展库_其一_用法讲解


    不依靠其他工具,直接使用Python的扩展API来编写一些简单的C扩展模块。

    本篇参考PythonCookbook第15节和Python核心编程完成,值得注意的是,Python2.X和Python3.X在扩展库写法上略有不同,我们研究的是3.X写法。

    一、源文件

    Extest2.c

    C函数本体

    c文件头必须包含"Python.h"头,以调用接口函数

    这里面写了两个c函数,模块名称定为Extest

    #include "Python.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int fac(int n)
    {
    	if (n<2) return(1);
    	return (n)*fac(n-1);
    }
    
    char *reverse(char *s)
    {
    	register char t,
    			 *p = s,
    		     *q = (s + (strlen(s)-1));
        while (p < q)
        {
        	t = *p;
        	*p++ = *q;
        	*q-- = t;
        }
        return s;
    }
    

    Python API封装

    对这两个函数采取C对Python API封装,

    • 封装函数为静态函数,输入返回均为PyObject*,且输入须有一个self用于处理类对自身的引用,函数名须为模块名_c函数名格式(模块名不是强制的)
      • static PyObject*
        模块名称_C函数名称(PyObject *self, PyObject *args)
    • Python对象->C对象,用于C函数输入,使用PyArg_ParseTuple为元组输入解析类型,另有PyArg_ParseTupleAndKeywords为字典类型解析类型
      • PyArg_ParseTuple(args, "i", &num)  //PyObject参数,类型指定,C参数存放地址1,[C参数存放地址2……]
    • C对象->Python对象,用于C函数输出,使用(PyObject*)Py_BuildValue进行类型转换
      • (PyObject*)Py_BuildValue("ss", orig_str, 
        		dupe_str = reverse(strdup(orig_str)));  //类型指定,C参数1,[C参数2……]
    • return Python对象,由于是个元组,所以上一步多少个C输出都没有关系
    static PyObject*
    Extest_fac(PyObject *self, PyObject *args)
    {
    	int num;
    	if (!PyArg_ParseTuple(args, "i", &num))
    		return NULL;
    	return (PyObject*)Py_BuildValue("i", fac(num));
    }
    
    static PyObject*
    Extest_doppel(PyObject *self, PyObject *args)
    {
    	char *orig_str;
    	char *dupe_str;
    	PyObject* retval;
    	if (!PyArg_ParseTuple(args, "s", &orig_str)) return NULL;
    	retval = (PyObject*)Py_BuildValue("ss", orig_str, 
    		dupe_str = reverse(strdup(orig_str)));
    	free(dupe_str);
    	return retval;
    }
    

    首先,在扩展模块中,你写的函数都是像下面这样的一个普通原型:

    static PyObject *py_func(PyObject *self, PyObject *args) {
      ...
    }
    

    PyObject 是一个能表示任何Python对象的C数据类型。 在一个高级层面,一个扩展函数就是一个接受一个Python对象 (在 PyObject *args中)元组并返回一个新Python对象的C函数。 函数的 self 参数对于简单的扩展函数没有被使用到, 不过如果你想定义新的类或者是C中的对象类型的话就能派上用场了。比如如果扩展函数是一个类的一个方法, 那么 self 就能引用那个实例了。

    PyArg_ParseTuple() 函数被用来将Python中的值转换成C中对应表示。 它接受一个指定输入格式的格式化字符串作为输入,比如“i”代表整数,“d”代表双精度浮点数, 同样还有存放转换后结果的C变量的地址。 如果输入的值不匹配这个格式化字符串,就会抛出一个异常并返回一个NULL值。 通过检查并返回NULL,一个合适的异常会在调用代码中被抛出。

    Py_BuildValue() 函数被用来根据C数据类型创建Python对象。 它同样接受一个格式化字符串来指定期望类型。 在扩展函数中,它被用来返回结果给Python。 Py_BuildValue() 的一个特性是它能构建更加复杂的对象类型,比如元组和字典。 在 py_divide() 代码中,一个例子演示了怎样返回一个元组。不过,下面还有一些实例:

    return Py_BuildValue("i", 34);      // Return an integer
    return Py_BuildValue("d", 3.4);     // Return a double
    return Py_BuildValue("s", "Hello"); // Null-terminated UTF-8 string
    return Py_BuildValue("(ii)", 3, 4); // Tuple (3, 4)
    

    库信息记录

     库信息以及初始化信息

    /* 记录函数信息,{函数在Python中名称,函数对应封装,参数格式(此处表示参数以元组格式传入)} */
    static PyMethodDef
    ExtestMethods[] = 
    {
        {"fac", Extest_fac, METH_VARARGS},
        {"doppel", Extest_doppel, METH_VARARGS},
        {NULL, NULL},
    };
    
    
    /* Module structure */
    static struct PyModuleDef Extestmodule = {
      PyModuleDef_HEAD_INIT,
    
      "Extest",           /* 库名称 */
      "A sample module",  /* Doc string (may be NULL) */
      -1,                 /* Size of per-interpreter state or -1 */
      ExtestMethods       /* 函数信息 */
    };
    
    /* Module initialization function */
    PyMODINIT_FUNC
    PyInit_Extest(void) {                      /* PyInit_库名称 */
      return PyModule_Create(&Extestmodule);   /* 参数为Module structure名词 */
    }
    

    在扩展模块底部,你会发现一个函数表,比如本节中的 ExtestMethods 表。 这个表可以列出C函数、Python中使用的名字、文档字符串。 所有模块都需要指定这个表,因为它在模块初始化时要被使用到。

    最后的函数 PyInit_Extest() 是模块初始化函数,但该模块第一次被导入时执行。 这个函数的主要工作是在解释器中注册模块对象。

    二、编译文件setup.py

    from distutils.core import setup, Extension
    
    MOD = "Extest"
    setup(name=MOD,  # 一个名字参数表示要编译哪个东西
        ext_modules=[ # 一个list对象列出编译对象
          Extension(MOD, sources=['Extest2.c']) # 完整扩展名(可能含有.),源文件
       ])

    三、测试调用

    调用:

    python setup.py build
    python setup.py install
    

     测试:

  • 相关阅读:
    泛型的二阶构造
    二叉树的优势
    浅谈AVL树,红黑树,B树,B+树原理及应用
    AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中?
    据库索引及其数据结构
    关系型数据库工作原理-数据结构(3)
    数据库的最简单实现
    为什么使用数据库?数据库的存取效率如何保证?
    iOS原生数据存储策略
    数据存储要解决的几个问题
  • 原文地址:https://www.cnblogs.com/hellcat/p/9081652.html
Copyright © 2020-2023  润新知