Python 扩展技术总结(转)
一般来说,所有能被整合或导入到其他Python脚本中的代码,都可以称为扩展。你可以用纯Python来写扩展,也可以用C/C++之类的编译型语言来写扩展,甚至可以用java,C都可以来写 python扩展。Python的一大特点是,扩展和解释器之间的交互方式域普通的Python模块完全一样,Python的模块导入机制非常抽象,抽象到让使用模块的代码无法了解到模块的具体实现细节。
Python进行扩展的主要原因有三点:(1)添加额外的Python语言的核心部分没有提供的功能 (2)为了提升性能瓶颈的效率(3)保持专有源代码私密,把一部分代码从Python转到编译语言可以保持专有源代码私密
方法一:利用C API进行C扩展
这种方法是最基本的,包括三个步骤:1.创建应用程序代码 2.利用样板来包装代码 3.编译1.创建一个Extest.c文件 包含两个C函数
Extest.c
[cpp] view plain copy
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define BUFSIZE 10
- int fac(int n) {
- if (n < 2)
- return 1;
- return n * fac(n - 1);
- }
- //字符串反转
- char *reverse(char *s) {
- register char t;
- char *p = s;
- char *q = (s + (strlen(s) - 1));
- while (p < q) {
- t = *p;
- *p++ = *q;
- *q-- = t;
- }
- return s;
- }
2.用样板来包装你的代码
使用样板分为4步:
1.添加Python的头文件
2.为每一个模块的每个函数增加一个形如PyObject* Module_func()的包装函数
3.为每一个模块增加一个形如PyMethodDef ModuleMethods[]的数组
4.增加模块初始化函数 void initModule()
第二步的处理需要一些技巧,你需要为所有想Python环境访问的函数增加一个静态函数。函数的返回值类型为 PyObject*,在Python的C语言扩展接口中,大部分函数都有一个或者多个参数为PyObject指针类型,并且返回值也大都为PyObject指针. 包装函数的用处是先把Python的值传递给C,然后调用C函数把函数的计算结果转换成Python 对象,然后返回给Python
第三步每一个数组都包含一个 函数信息,包括函数Python的名字,相应的包装函数的名字以及一个METH_VARARGS常量(表示参数以元组的形式传入)
Extest.c
[cpp] view plain copy
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "Python.h" //包含python头文件
- #define BUFSIZE 10
- int fac(int n) {
- if (n < 2)
- return 1;
- return n * fac(n - 1);
- }
- char *reverse(char *s) {
- register char t;
- char *p = s;
- char *q = (s + (strlen(s) - 1));
- while (p < q) {
- t = *p;
- *p++ = *q;
- *q-- = t;
- }
- return s;
- }
- //fac函数的包装函数
- 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));
- }
- //reverse函数的包装函数
- static PyObject *
- Extest_doppel(PyObject *self, PyObject *args) {
- char *orignal;
- char *reversed;
- PyObject * retval;
- if (!(PyArg_ParseTuple(args, "s", &orignal))) {
- return NULL;
- }
- retval = (PyObject *)Py_BuildValue("ss", orignal, reversed=reverse(strdup(orignal)));
- free(reversed);
- return retval;
- }
- //为模块创建一个函数信息的数组
- static PyMethodDef
- ExtestMethods[] = {
- {"fac", Extest_fac, METH_VARARGS},
- {"doppel", Extest_doppel, METH_VARARGS},
- };
- //增加模块初始化函数
- void initExtest() {
- Py_InitModule("Extest", ExtestMethods);
- }
在编译阶段需要创建一个setup.py,通过setup.py来编译和链接代码。这一步完成后就就可以直接导入这个扩展的模块了.在setup.py中需要导入distutils包。首先你要为每一个扩展创建一个Extension实例,然后编译的操作主要由setup()函数来完成,它需要两个参数:一个名字参数表示要编译哪个东西,一个列表表示要编译的对象
setup.py
[cpp] view plain copy
- <pre name="code" class="python">from distutils.core import setup, Extension
- MOD = 'Extest'
- setup(name=MOD, ext_modules=[
- Extension(MOD, sources=['Extest.c'])])
运行setup.py :执行命令 python setup.py build
执行的结果 是在当前目录中生成一个build 目录,在此目录下有一个Extest.so文件,然后就可以在脚本中import这个模块了
方法二:利用Ctypes进行C扩展
为了扩展Python,我们可以用C/C++编写模块,但是这要求对Python的底层有足够的了解,包括Python对象模
型、常用模块、引用计数等,门槛较高,且不方便利用现有的C库。而ctypes 则另辟蹊径,通过封装
dlopen/dlsym之类的函数,并提供对C中数据结构的包装/解包,让Python能够加载动态库、导出其中的函数直
接加以利用。
一个简单的实例:
这个例子直接利用ctypes使用C标准库函数而不用编写C代码,使用C标准库函数会产生优化效果
脚本一
[python] view plain copy
- import timeit
- import random
- def generate(num):
- while num:
- yield random.randrange(10)
- num -= 1
- print(timeit.timeit("sum(generate(999))", setup="from __main__ import generate", number=1000))
[python] view plain copy
- <span style="font-size:18px;color:#FF0000;">脚本二</span>
- import timeit
- from ctypes import cdll
- def generate_c(num):
- #Load standard C library
- libc = cdll.LoadLibrary("libc.so.6") #Linux
- # libc = cdll.msvcrt #Windows
- while num:
- yield libc.rand() % 10
- num -= 1
- print(timeit.timeit("sum(generate_c(999))", setup="from __main__ import generate_c", number=1000))
第一个脚本使用 Python的随机函数,运行时间约为1.067秒 第二个脚本使用C的随机函数 运行时间约为0.423秒
我们也利用Ctypes可以自己写模块导入使用:
步骤一创建应用程序代码
[cpp] view plain copy
- /* functions.c */
- #include "stdio.h"
- #include "stdlib.h"
- #include "string.h"
- /* http://rosettacode.org/wiki/Sorting_algorithms/Merge_sort#C */
- inline
- void merge(int *left, int l_len, int *right, int r_len, int *out)
- {
- int i, j, k;
- for (i = j = k = 0; i < l_len && j < r_len; )
- out[k++] = left[i] < right[j] ? left[i++] : right[j++];
- while (i < l_len) out[k++] = left[i++];
- while (j < r_len) out[k++] = right[j++];
- }
- /* inner recursion of merge sort */
- void recur(int *buf, int *tmp, int len)
- {
- int l = len / 2;
- if (len <= 1) return;
- /* note that buf and tmp are swapped */
- recur(tmp, buf, l);
- recur(tmp + l, buf + l, len - l);
- merge(tmp, l, tmp + l, len - l, buf);
- }
- /* preparation work before recursion */
- void merge_sort(int *buf, int len)
- {
- /* call alloc, copy and free only once */
- int *tmp = malloc(sizeof(int) * len);
- memcpy(tmp, buf, sizeof(int) * len);
- recur(buf, tmp, len);
- free(tmp);
- }
- int fibRec(int n){
- if(n < 2)
- return n;
- else
- return fibRec(n-1) + fibRec(n-2);
- }
- ~
步骤二:将它编译链接为.so文件
执行指令:gcc -Wall -fPIC -c functions.c
gcc -shared -o libfunctions.so functions.o
步骤三:在自己写的python脚本中使用这些库,下面的脚本分别用纯python的模块(输出时前面加了python),和我们导入的扩展(输出时前面加了C)模块来运行,对比其模块执行时间,
functions.py
[python] view plain copy
- from ctypes import *
- import time
- libfunctions = cdll.LoadLibrary("./libfunctions.so")
- def fibRec(n):
- if n<2 :
- return n
- else:
- return fibRec(n-1) + fibRec(n-2)
- start = time.time()
- fibRec(32)
- finish = time.time()
- print("Python: " + str(finish - start))
- #C Fibonacci
- start = time.time()
- x = libfunctions.fibRec(32)
- finish = time.time()
- print("C: " + str(finish - start))
functions2.py
[python] view plain copy
- from ctypes import *
- import time
- libfunctions = cdll.LoadLibrary("./libfunctions.so")
- #Python Merge Sort
- from random import shuffle, sample
- #Generate 9999 random numbers between 0 and 100000
- numbers = sample(range(100000), 9999)
- shuffle(numbers)
- c_numbers = (c_int * len(numbers))(*numbers)
- from heapq import merge
- def merge_sort(m):
- if len(m) <= 1:
- return m
- middle = len(m) // 2
- left = m[:middle]
- right = m[middle:]
- left = merge_sort(left)
- right = merge_sort(right)
- return list(merge(left, right))
- start = time.time()
- numbers = merge_sort(numbers)
- finish = time.time()
- print("Python: " + str(finish - start))
- #C Merge Sort
- start = time.time()
- libfunctions.merge_sort(byref(c_numbers), len(numbers))
- finish = time.time()
- print("C: " + str(finish - start))
- ~
- ~
- ~
运行结果
可以看出纯python模块的运行时间是我们写的扩展模块运行时间的十倍以上
方法三:利用Cython 进行C扩展 (参考Cython三分钟入门)
Cyhton 是一个用来快速生成 Python 扩展模块(extention module)的工具,它的语法是 python语言语法和 C 语言语法的混血。准确说 Cython 是单独的一门语言,专门用来写在 Python 里面import 用的扩展库。实际上 Cython 的语法基本上跟 Python 一致,而Cython 有专门的“编译器”;先将 Cython 代码转变成 C(自动加入了一大堆的 C-Python API),然后使用 C 编译器编译出最终的 Python可调用的模块。
要注意的一点是, Cython 是用来生成 C 扩展到而不是独立的程序的。所有的加速都是针对一个已经存在的 Python 应用的一个函数进行的。没有使用 C 或 Lisp 重写整个应用程序,也没有手写 C 扩展 。只是用一个简单的方法来整合 C 的速度和 C 数据类型到 Python函数中去
Cython 代码跟 Python 不一样,必须要编译。 编译经过两个阶段:(1) Cython 编译.pyx 文件为.c 文件 (2) C 编译器会把.c 文件编译成.so 文件.生成.so 文件后表示重写函数成功,可以在 python 代码中直接调用这个模块
Python代码
- #p1.py
- import math
- def great_circle(lon1,lat1,lon2,lat2):
- radius = 3956 #miles
- x = math.pi/180.0
- a = (90.0-lat1)*(x)
- b = (90.0-lat2)*(x)
- theta = (lon2-lon1)*(x)
- c = math.acos((math.cos(a)*math.cos(b)) +
- (math.sin(a)*math.sin(b)*math.cos(theta)))
- return radius*c
Python代码
- #p1_test.py
- import timeit
- lon1, lat1, lon2, lat2 = -72.345, 34.323, -61.823, 54.826
- num = 500000
- t = timeit.Timer("p1.great_circle(%f,%f,%f,%f)" % (lon1,lat1,lon2,lat2),
- "import p1")
- print "Pure python function", t.timeit(num), "sec"
Python代码
- # 测试结果
- Pure python function 2.25580382347 sec
Cython: 使用Python的math模块
Python代码
- #c1.pyx
- import math
- def great_circle(float lon1,float lat1,float lon2,float lat2):
- cdef float radius = 3956.0
- cdef float pi = 3.14159265
- cdef float x = pi/180.0
- cdef float a,b,theta,c
- a = (90.0-lat1)*(x)
- b = (90.0-lat2)*(x)
- theta = (lon2-lon1)*(x)
- c = math.acos((math.cos(a)*math.cos(b)) + (math.sin(a)*math.sin(b)*math.cos(theta)))
- return radius*c
Python代码
- # setup.py
- from distutils.core import setup
- from distutils.extension import Extension
- from Cython.Distutils import build_ext
- ext_modules=[
- Extension("c1",
- ["c1.pyx"])
- ]
- setup(
- name = "Demos",
- cmdclass = {"build_ext": build_ext},
- ext_modules = ext_modules
- )
python setup.py build_ext --inplace
Python代码
- #c1_test.py
- import timeit
- lon1, lat1, lon2, lat2 = -72.345, 34.323, -61.823, 54.826
- num = 500000
- t = timeit.Timer("c1.great_circle(%f,%f,%f,%f)" % (lon1,lat1,lon2,lat2),
- "import c1")
- print "Pure python function", t.timeit(num), "sec"
Python代码
- #执行结果:
- Pure python function 1.87078690529 sec
Cython:使用C的math库
Python代码
- #c2.pyx
- cdef extern from "math.h":
- float cosf(float theta)
- float sinf(float theta)
- float acosf(float theta)
- def great_circle(float lon1,float lat1,float lon2,float lat2):
- cdef float radius = 3956.0
- cdef float pi = 3.14159265
- cdef float x = pi/180.0
- cdef float a,b,theta,c
- a = (90.0-lat1)*(x)
- b = (90.0-lat2)*(x)
- theta = (lon2-lon1)*(x)
- c = acosf((cosf(a)*cosf(b)) + (sinf(a)*sinf(b)*cosf(theta)))
- return radius*c
Python代码
- #setup.py
- from distutils.core import setup
- from distutils.extension import Extension
- from Cython.Distutils import build_ext
- ext_modules=[
- Extension("c2",
- ["c2.pyx"],
- libraries=["m"]) # Unix-like specific
- ]
- setup(
- name = "Demos",
- cmdclass = {"build_ext": build_ext},
- ext_modules = ext_modules
- )
python setup.py build_ext --inplace
Python代码
- # c2_test.py
- import timeit
- lon1, lat1, lon2, lat2 = -72.345, 34.323, -61.823, 54.826
- num = 500000
- t = timeit.Timer("c2.great_circle(%f,%f,%f,%f)" % (lon1,lat1,lon2,lat2),
- "import c2")
- print "Pure python function", t.timeit(num), "sec"
Python代码
- #执行结果
- Pure python function 0.34069108963 sec
Cython:使用C函数
Python代码
- #c3.pyx
- cdef extern from "math.h":
- float cosf(float theta)
- float sinf(float theta)
- float acosf(float theta)
- cdef float _great_circle(float lon1,float lat1,float lon2,float lat2):
- cdef float radius = 3956.0
- cdef float pi = 3.14159265
- cdef float x = pi/180.0
- cdef float a,b,theta,c
- a = (90.0-lat1)*(x)
- b = (90.0-lat2)*(x)
- theta = (lon2-lon1)*(x)
- c = acosf((cosf(a)*cosf(b)) + (sinf(a)*sinf(b)*cosf(theta)))
- return radius*c
- def great_circle(float lon1,float lat1,float lon2,float lat2,int num):
- cdef int i
- cdef float x
- for i from 0 <= i < num:
- x = _great_circle(lon1,lat1,lon2,lat2)
- return x
C-sharp代码
- #setup.py
- from distutils.core import setup
- from distutils.extension import Extension
- from Cython.Distutils import build_ext
- ext_modules=[
- Extension("c3",
- ["c3.pyx"],
- libraries=["m"]) # Unix-like specific
- ]
- setup(
- name = "Demos",
- cmdclass = {"build_ext": build_ext},
- ext_modules = ext_modules
- )
python setup.py build_ext --inplace
Python代码
- #c3_test.py
- import timeit
- lon1, lat1, lon2, lat2 = -72.345, 34.323, -61.823, 54.826
- num = 500000
- t = timeit.Timer("c2.great_circle(%f,%f,%f,%f)" % (lon1,lat1,lon2,lat2),
- "import c2")
- print "Pure python function", t.timeit(num), "sec"
Python代码
- #测试结果
- Pure python function 0.340164899826 sec
测试结论
Python代码
- # python代码
- Pure python function 2.25580382347 sec
- # Cython,使用Python的math模块
- Pure python function 1.87078690529 sec
- # Cython,使用C的math库
- Pure python function 0.34069108963 sec
- # Cython,使用纯粹的C函数
- Pure python function 0.340164899826 sec
注意事项:
通过cython扩展python 模块时出现“ImportError: No module named Cython.Build“的解决方法如下:
pip install Cython
pip install fasttext
这个pip必须与当前python版本相一致
参考: https://blog.csdn.net/u010786109/article/details/41825147#
Python 扩展技术总结(转)的更多相关文章
- vs写python扩展资料收集
总结: 1.创建dll工程: 2.增加包含头文件路径 :将python路径下的include加入到包含头文件路径:在工程属性页 C/C++/附加包含目新增<Python>\include目 ...
- Python基础+Pythonweb+Python扩展+Python选修四大专题 超强麦子学院Python35G视频教程
[保持在百度网盘中的, 可以在观看,嘿嘿 内容有点多,要想下载, 回复后就可以查看下载地址,资源收集不易,请好好珍惜] 下载地址:http://www.fu83.cc/ 感觉文章好,可以小手一抖 -- ...
- Python开发技术详解PDF
Python开发技术详解(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1F5J9mFfHKgwhkC5KuPd0Pw 提取码:xxy3 复制这段内容后打开百度网盘手 ...
- LFD,非官方的Windows二进制文件的Python扩展包
LFD,非官方的Windows二进制文件的Python扩展包 LFD,非官方版本.32和64位.Windows.二进制文件.科学开源.Python扩展包 克里斯托夫·戈尔克(by Christoph ...
- 《python开发技术详解》|百度网盘免费下载|Python开发入门篇
<python开发技术详解>|百度网盘免费下载|Python开发入门篇 提取码:2sby 内容简介 Python是目前最流行的动态脚本语言之一.本书共27章,由浅入深.全面系统地介绍了利 ...
- python自动化测试(4)-使用第三方python库技术实现
python自动化测试(4)-使用第三方python库技术实现 1 概述 关于测试的方法论,都是建立在之前的文章里面提到的观点: 功能测试不建议做自动化 接口测试性价比最高 接口测试可以做自动化 ...
- CSS3扩展技术
我们使用扩展技术编写代码时,需要先用编译器将我们的文件进行编译,编译后的文件才能够使用. less技术相关语法 less相对来说比较简单,语法也较少: 变量的定义: @w:20px; ...
- python扩展实现方法--python与c混和编程 转自:http://www.cnblogs.com/btchenguang/archive/2012/09/04/2670849.html
前言 需要扩展Python语言的理由: 创建Python扩展的步骤 1. 创建应用程序代码 2. 利用样板来包装代码 a. 包含python的头文件 b. 为每个模块的每一个函数增加一个型如PyObj ...
- window下python 扩展库安装 使用第三方镜像源
0.前言 由于python的官方镜像位于国外,若使用pip或者easy_install安装第三方插件时或许会被限制,甚至连easy_install或pip也无法安装,例如在windows环境下 ...
随机推荐
- 访问tp3.2的项目时出现No input file specified.的解决办法
解决办法很简单如下: 打开.htaccess 在RewriteRule 后面的index.php教程后面添加一个“?” 原来的代码如下 <IfModule mod_rewrite.c> O ...
- D - 文理分科 HYSBZ - 3894(最小割)
题目链接:https://cn.vjudge.net/contest/281959#problem/D 题目大意:中文题目 具体思路: 首先说一下最小割:在最小代价的前提下,删除一些边之后,能够使得整 ...
- ACM-ICPC 2018 南京赛区网络预赛 L题(分层图,堆优化)
题目链接: https://nanti.jisuanke.com/t/31001 超时代码: #include<bits/stdc++.h> using namespace std; # ...
- HTTP协议-响应报文格式
HTTP协议-响应码 浏览器向服务器发出请求,服务器处理可能是成功.可能是失败.可能没有权限访问等原因,服务器会通过响应码来告诉浏览器处理结果. " : OK " : Found ...
- python3之协程
1.协程的概念 协程,又称微线程,纤程.英文名Coroutine. 线程是系统级别的它们由操作系统调度,而协程则是程序级别的由程序根据需要自己调度.在一个线程中会有很多函数,我们把这些函数称为子程序, ...
- motor的使用
# -*- coding: utf-8 -*- # @Time : 2018/11/18 10:41 PM # @Author : cxa # @File : motordb.py # @Softwa ...
- 命令查看WebSphere MQ运行状态
参考:https://wenku.baidu.com/view/34e40e2ffd0a79563c1e72b9.html 一.查看队列管理器运行状态 # dspmq 显示结果中QMNAME表示MQ队 ...
- zookeeper3.4.6配置实现自动清理日志
在使用zookeeper过程中,我们知道,会有dataDir和dataLogDir两个目录,分别用于snapshot和事务日志的输出(默认情况下只有dataDir目录,snapshot和事务日志都保存 ...
- IE中window的模态框与返回值
window.returnValue是javascript中html的window对象的属性,目的是返回窗口值,当用window.showModalDialog函数打开一个IE的模态窗口时,用于返回窗 ...
- 基于url拦截