python核心编程学习笔记-2016-09-25-01-python扩展
来源:互联网 发布:股票数据库 编辑:程序博客网 时间:2024/04/30 00:06
22.1 引言/动机
22.1.1 什么是扩展
扩展:所有能被整合或导入到其他python脚本的代码,都可以称为扩展。e.g. 纯python、C/C++、Java、C#或Visual Basic.NET都可以
22.1.2 为什么要扩展python
- 添加/额外的(非python)功能
- 性能瓶颈的效率提升——把软件开发过程中的瓶颈部分在扩展中实现
- 保持专有源代码
22.2 创建python扩展(以C为例)
一般过程:
- 创建应用程序;
- 利用样板来包装代码;
- 编译与测试。
22.2.1 创建应用程序
即创建在Python内运行的一个模块。在本步骤中,最好尽可能完善代码,否则不利于查找bug。
Extest1.c
#include<stdio.h>#include<stdlib.h>#include<string.h>int fac(int n){ if (n < 2) return (1); /* 0! == 1! == 1 */ return (n) * fac(n-1); /* n! == n * (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;}int main(){ char s[BUFSIZ]; printf("4! == %d\n", fac(4)); printf("8! == %d\n", fac(8)); printf("12! == %d\n", fac(12)); strcpy(s, "abcdef"); printf("reversing 'abcdef', we get '%s'\n", reverse(s)); strcpy(s, "madam"); printf("reversing 'madam', we get '%s'\n", reverse(s)); return 0;}22.2.2 用样板来包装代码
样板代码:接口的代码被称为样板代码。
主要分为四个步骤:
- 包含Python的头文件;
- 为每一个模块的每个函数增加一个刑如PyObject* Module_func()的包装函数;
- 为每个模块增加一个形如PyMethodDef ModuleMethods[]的数组;
- 增加模块初始化函数void initModule()
在上述C代码中增加一行:(其他因为用的不是Unix系统,不太懂)
#include "Python.h"2. 为每一个模块的每个函数增加一个刑如PyObject* Module_func()的包装函数
需要为所有想被Python环境访问的函数增加一个静态函数(?),函数返回类型是PyObject*,函数名前要加上模块名和一个下划线。
从后面来看就是,与普通的Python模块导入没有什么差别,即在Python解释器中能先import Extest,再通过Extest.fac()来访问上述C代码中的fac()函数。
包装函数的用处就是,先把Python的值传给C,然后调用相关函数,得到相应的结果,再把这个结果转换成Python对象,传回给Python。从Python到C,要用到PyArg_Parse*()函数,从C到Python要用到Py_BuildValue()函数。
PyArg_Parse*()函数接受一个字符串流,并根据一个指定的格式字符串进行解析,把结果放入到相应的指针所指的变量中。若返回1,表示解析成功,返回0,表示解析失败。
Py_BuildValue(),把所有的参数按格式字符串所指定的格式转换成一个Python对象。
函数描述int PyArg_ParseTuple()把Python传过来的参数转为Cint PyArg_ParseTupleAndKeyWords()同上,但同时解析关键字参数Py_BuildValue()把C的数据转为Python的一个或一组对象,然后返回
格式代码Python型C/C++型sstrchar*zstr/Nonechar*/NULLiintintllonglongcstrchardfloatdoubleDcomplexPy_Complex*O(any)PyObject*SstrPyStringObject
Extest2.c
#include<stdio.h>#include<stdlib.h>#include<string.h>int fac(int n){ if (n < 2) return (1); /* 0! == 1! == 1 */ return (n) * fac(n-1); /* n! == n * (n-1)!*/}char *reverse(char *s){ register char t, *p = s, *q = (s + (strlen(s)-1)); while (s &&(p < q)) { t = *p; *p++ = *q; *q-- = t; } return s;}int test(){ char s[BUFSIZ]; printf("4! == %d\n", fac(4)); printf("8! == %d\n", fac(8)); printf("12! == %d\n", fac(12)); strcpy(s, "abcdef"); printf("reversing 'abcdef', we get '%s'\n", reverse(s)); strcpy(s, "madam"); printf("reversing 'madam', we get '%s'\n", reverse(s)); return 0;}#include "Python.h" // Python头文件// 两个包装函数Extest_fac和Extest_doppelstatic PyObject *Extest_fac(PyObject *self, PyObject *args){ int num; // 解析结果 if (!PyArg_ParseTuple(args, "i", &num)) // PyArg_ParseTuple(args, "i", &num)就是将传入的数据解析,i表示希望得到整型变量,如果传入正确,将它保存到num变量中 return NULL; // 如果传入错误,返回NULL return (PyObject*)Py_BuildValue("i", fac(num)); // 直接将结果转为Python的整型返回}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))); // 'ss'就表示返回一个含有两个字符串的元组,strdup是复制字符串,要复制一份原始字符串,不理解。 free(dupe_str); // 释放内存, return retval;}// 测试函数,将Extest1中的main()改为test()static PyObject *Extest_test(PyObject *self, PyObject *args){ test(); return (PyObject*)Py_BuildValue(""); // 返回空字符串给Python}// 把包装函数列在某个地方,以便Python解释器能够导入并调用static PyMethodDefExtestMethods[] = // 该数组由多个二维数组组成,每个数组包含一个函数的信息,最后的NULL表示列表的结束{ {"fac", Extest_fac, METH_VARARGS }, // 第一项是函数在Python中的名字,第二项是相应的包装函数的名字,第三项是METH_VARARGS常量,该常量表示参数以元组形式传入 {"doppel", Extest_doppel, METH_VARARGS }, {"test", Extest_test, METH_VARARGS }, {NULL, NULL },};// 模块初始化函数void initModule(),这部分代码在模块导入时被解释器调用void initExtest(){ Py_InitModule("Extest", ExtestMethods);}22.2.3 编译
使用distutils模块,该模块被用于编译、安装和分发模块、扩展和包。
主要步骤:
- 创建setup.py;
- 通过运行setup.py来编译和连接代码;
- 从Python中导入模块;
- 测试功能。
#-*-coding: utf-8-*-from distutils.core import setup, ExtensionMOD = 'Extest'setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest2.c'])]) # 在编译扩展之前,需为每一个扩展创建Extension实例,本例中是'Extest',后面跟的sources是所有源代码的文件列表,本例中是'Extest2.c'# setup()需两个参数,一个是名字参数,表示要编译哪个东西,另外一个是列表,列出要编译的对象
22.2.5 引用计数
两类引用:拥有引用和借引用。(这部分不知道要干嘛)
22.2.6 线程和全局解释器锁(GIL) (同上)
22.2.7 相关话题
嵌入:把Python解释器包装到C的程序中。
0 0
- python核心编程学习笔记-2016-09-25-01-python扩展
- Python-核心编程-学习笔记
- 《python核心编程》学习笔记
- 《Python核心编程》学习笔记
- 《python核心编程》学习笔记
- python学习笔记--python核心编程
- python核心编程学习笔记-2016-07-18-01-print
- python核心编程学习笔记-2016-08-26-01-文件传输
- Python核心编程学习笔记-Python核心编程第七章
- python核心编程学习笔记-2016-09-03-01-图形化用户界面编程(二)
- python核心编程学习笔记-2016-09-10-01-Web编程(一)
- python核心编程学习笔记-2016-09-11-01-Web编程(四)
- python核心编程学习笔记-2016-09-14-01-Web编程(五)
- python核心编程学习笔记-2016-09-17-01-数据库编程(一)
- python核心编程学习笔记-2016-09-18-01-数据库编程(三)
- python核心编程学习笔记-2016-09-24-01-数据库编程(四)
- python核心编程笔记01
- python核心编程学习笔记-2016-09-15-01-urlopen返回的对象无seek()方法
- Linux安装Java JDK 1.7
- Redis详解
- 那些我曾经犯过的低级编程错误
- Lambda 与 linq 区别
- 第五周项目一(1)求圆的周长
- python核心编程学习笔记-2016-09-25-01-python扩展
- 闭包,懂不懂由你,反正我是懂了
- 微信底部菜单栏实现的几种方法 -- Android学习之路
- 关于Listener监听的session的定时销毁
- 数据类型
- 2-6Hadoop RPC(Hadoop系列day02)
- ubuntu12.10桥接模式上网设置
- codevs1378 选课 树形dp
- Hive_10. Hive中常用的 SerDe 和 当前社区的状态