『Python CoolBook』使用ctypes访问C代码_下_demo进阶
这一次我们尝试一下略微复杂的c程序。
一、C程序
头文件:
#ifndef __SAMPLE_H__
#define __SAMPLE_H__
#include <math.h> #ifdef __cplusplus
extern "C" {
#endif int gcd(int x, int y);
int in_mandel(double x0, double y0, int n);
int divide(int a, int b, int *remainder);
double avg(double *a, int n); /* A C data structure */
typedef struct Point {
double x,y;
} Point; double distance(Point *p1, Point *p2); #ifdef __cplusplus
}
#endif
#endif
源程序:
divide()
函数是一个返回多个值的C函数例子,其中有一个是通过指针参数的方式。
avg()
函数通过一个C数组执行数据聚集操作。
Point
和 distance()
函数涉及到了C结构体。
/* sample.c */
#include "sample.h" /* Compute the greatest common divisor */
int gcd(int x, int y) {
int g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
} /* Test if (x0,y0) is in the Mandelbrot set or not */
int in_mandel(double x0, double y0, int n) {
double x=0,y=0,xtemp;
while (n > 0) {
xtemp = x*x - y*y + x0;
y = 2*x*y + y0;
x = xtemp;
n -= 1;
if (x*x + y*y > 4) return 0;
}
return 1;
} /* Divide two numbers */
int divide(int a, int b, int *remainder) {
int quot = a / b;
*remainder = a % b;
return quot;
} /* Average values in an array */
double avg(double *a, int n) {
int i;
double total = 0.0;
for (i = 0; i < n; i++) {
total += a[i];
}
return total / n;
} /* Function involving a C data structure */
double distance(Point *p1, Point *p2) {
return hypot(p1->x - p2->x, p1->y - p2->y);
}
生成so文件后,我们尝试调用这些方法。
二、Python封装
.argtypes
属性是一个元组,包含了某个函数的输入按时, 而 .restype
就是相应的返回类型。
ctypes
定义了大量的类型对象(比如c_double, c_int, c_short, c_float等), 代表了对应的C数据类型。
如果你想让Python能够传递正确的参数类型并且正确的转换数据的话, 那么这些类型签名的绑定是很重要的一步。如果你没有这么做,不但代码不能正常运行, 还可能会导致整个解释器进程挂掉。
导入c库文件
import os
import ctypes _mod = ctypes.cdll.LoadLibrary('./libsample.so')
简单数据类型函数封装
实际上由于这种函数的参数类型c语言和python语言中的类型是一一对应的,所以即使把.argtypes与
.restype
注释掉也可以正常运行,但建议进行转换
gcd:
原函数
/* Compute the greatest common divisor */
int gcd(int x, int y) {
int g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
}
调用
# int gcd(int, int)
gcd = _mod.gcd
gcd.argtypes = (ctypes.c_int, ctypes.c_int)
gcd.restype = ctypes.c_int print(gcd(35, 42)) # 7
in_mandel:
原函数
/* Test if (x0,y0) is in the Mandelbrot set or not */
int in_mandel(double x0, double y0, int n) {
double x=0,y=0,xtemp;
while (n > 0) {
xtemp = x*x - y*y + x0;
y = 2*x*y + y0;
x = xtemp;
n -= 1;
if (x*x + y*y > 4) return 0;
}
return 1;
}
调用
# int in_mandel(double, double, int)
in_mandel = _mod.in_mandel
in_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int)
in_mandel.restype = ctypes.c_int print(in_mandel(0,0,500)) # 1
含有指针形参函数--指针用于修改变量
divide()
函数通过一个参数除以另一个参数返回一个结果值,但是指针是python中不支持的操作。
/* Divide two numbers */
int divide(int a, int b, int *remainder) {
int quot = a / b;
*remainder = a % b;
return quot;
}
对于涉及到指针的参数,你通常需要先构建一个相应的ctypes对象并像下面这样传进去:
divide = _mod.divide
x = ctypes.c_int()
divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
print(divide(10,3,x))
print(x.value)
在这里,一个 ctypes.c_int
实例被创建并作为一个指针被传进去。 跟普通Python整形不同的是,一个 c_int
对象是可以被修改的。 .value
属性可被用来获取或更改这个值。
更一般的,对于带指针的函数,我们会将其加一层封装后调用,使得通过指针修改的变量通过return返回,这样去c style,使得代码更像python风格:
# int divide(int, int, int *)
_divide = _mod.divide
_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
_divide.restype = ctypes.c_int def divide(x, y):
rem = ctypes.c_int()
quot = _divide(x,y,rem)
return quot, rem.value
含有指针形参函数--指针用于接收数组
avg()
函数又是一个新的挑战。C代码期望接受到一个指针和一个数组的长度值。 但是,在Python中,我们必须考虑这个问题:数组是啥?它是一个列表?一个元组? 还是 array
模块中的一个数组?还是一个 numpy
数组?还是说所有都是? 实际上,一个Python“数组”有多种形式,你可能想要支持多种可能性。
/* Average values in an array */
double avg(double *a, int n) {
int i;
double total = 0.0;
for (i = 0; i < n; i++) {
total += a[i];
}
return total / n;
}
python -> c数组
(ctypes.c_int * 数组长度)(数组元素)
内在逻辑是:对于列表和元组,from_list
方法将其转换为一个 ctypes
的数组对象
nums = [1, 2, 3]
a = (ctypes.c_int * len(nums))(3,4,5)
print(a)
print(a[0], a[1], a[2]) # <__main__.c_int_Array_3 object at 0x7f2767d4fd08>
# 3 4 5
array对象本身存储结构和c数组一致,直接提取内存地址传给c指针即可:
import array
a = array.array('d',[1,2,3])
print(a)
ptr = a.buffer_info() # 返回tuple:(地址, 长度)
print(ptr[0])
print(ctypes.cast(ptr[0], ctypes.POINTER(ctypes.c_double))) # 目标地址存入指针
numpy数组自带ctypes.data_as(ctypes指针)方法,更为方便。
有如上基础,我们可以做出将python序列转化为c数组指针的class(这个class我没有完全弄懂其含义),并封装avg函数:
# void avg(double *, int n)
# Define a special type for the 'double *' argument
class DoubleArrayType:
def from_param(self, param):
typename = type(param).__name__
if hasattr(self, 'from_' + typename):
return getattr(self, 'from_' + typename)(param)
elif isinstance(param, ctypes.Array):
return param
else:
raise TypeError("Can't convert %s" % typename) # Cast from array.array objects
def from_array(self, param):
if param.typecode != 'd':
raise TypeError('must be an array of doubles')
ptr, _ = param.buffer_info()
return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double)) # Cast from lists/tuples
def from_list(self, param):
val = ((ctypes.c_double)*len(param))(*param)
return val from_tuple = from_list # Cast from a numpy array
def from_ndarray(self, param):
return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) DoubleArray = DoubleArrayType()
_avg = _mod.avg
_avg.argtypes = (DoubleArray, ctypes.c_int)
_avg.restype = ctypes.c_double def avg(values):
return _avg(values, len(values))
结构体
/* A C data structure */
typedef struct Point {
double x,y;
} Point; /* Function involving a C data structure */
double distance(Point *p1, Point *p2) {
return hypot(p1->x - p2->x, p1->y - p2->y);
}
继承ctypes.Structure,_fields_命名结构体内元素即可:
# struct Point { }
class Point(ctypes.Structure):
_fields_ = [('x', ctypes.c_double),
('y', ctypes.c_double)] # double distance(Point *, Point *)
distance = _mod.distance
distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))
distance.restype = ctypes.c_double
>>> p1 = Point(1,2)
>>> p2 = Point(4,5)
>>> p1.x
1.0
>>> p1.y
2.0
>>> distance(p1,p2)
4.242640687119285
>>>
『Python CoolBook』使用ctypes访问C代码_下_demo进阶的更多相关文章
- 『Python CoolBook』使用ctypes访问C代码_上_用法讲解
一.动态库文件生成 源文件hello.c #include "hello.h" #include <stdio.h> void hello(const char *na ...
- 『Python CoolBook』Cython
github地址 使用Cython导入库的话,需要一下几个文件: .c:C函数源码 .h:C函数头 .pxd:Cython函数头 .pyx:包装函数 setup.py:python 本节示例.c和.h ...
- 『Python CoolBook』Cython_高效数组操作
数组运算加速是至关科学计算重要的领域,本节我们以一个简单函数为例,使用C语言为python数组加速. 一.Cython 本函数为一维数组修剪最大最小值 version1 @cython.boundsc ...
- 『Python CoolBook』C扩展库_其一_用法讲解
不依靠其他工具,直接使用Python的扩展API来编写一些简单的C扩展模块. 本篇参考PythonCookbook第15节和Python核心编程完成,值得注意的是,Python2.X和Python3. ...
- 『Python CoolBook』C扩展库_其二_demo演示
点击进入项目 C函数源文件 /* sample.c */ #include "sample.h" /* Compute the greatest common divisor */ ...
- 『Python CoolBook』C扩展库_其三_简单数组操作
点击进入项目 这里的数组要点在于: 数组结构,array.array或者numpy.array 本篇的数组仅限一维,不过基础的C数组也是一维 一.分块讲解 源函数 /* Average values ...
- 『Python CoolBook』C扩展库_其四_结构体操作与Capsule
点击进入项目 一.Python生成C语言结构体 C语言中的结构体传给Python时会被封装为胶囊(Capsule), 我们想要一个如下结构体进行运算,则需要Python传入x.y两个浮点数, type ...
- 『Python CoolBook』C扩展库_其五_C语言层面Python库之间调用API
点击进入项目 一.C层面模块添加API 我们仍然操作如下结构体, #include <math.h> typedef struct Point { double x,y; } Point; ...
- 『Python CoolBook』C扩展库_其六_从C语言中调用Python代码
点击进入项目 一.C语言运行pyfun的PyObject对象 思路是在C语言中提供实参,传给python函数: 获取py函数对象(PyObject),函数参数(C类型) 获取GIL(PyGILStat ...
随机推荐
- 深入理解Spring AOP之二代理对象生成
深入理解Spring AOP之二代理对象生成 spring代理对象 上一篇博客中讲到了Spring的一些基本概念和初步讲了实现方法,当中提到了动态代理技术,包含JDK动态代理技术和Cglib动态代理 ...
- Django组件——分页器和中间件
分页器 Django内置分页器(paginator) 分页器函数为paginator,里面有几个重要的参数需要我们了解 paginator = Paginator(book_list, 10) #第二 ...
- java框架之Hibernate(1)-简介及初使用
简介 hibernate 是一个开源 ORM ( Object / Relationship Mipping ) 框架,它是对象关联关系映射的持久层框架,它对 JDBC 做了轻量级的封装,而我们 ja ...
- 110A
#include <stdio.h> #include<string.h> #define MAXSIZE 30 int main() { char digits[30]; m ...
- data日期转化
eg: var time="2018-05-19T08:04:52.000+0000"; var d = new Date(time); var times=d.getF ...
- i++与++i区别
++i:先执行i加1,再执行语句; i++:先执行语句,再i加1; 后置自操作:先把自己所保存的值留下来,然后改变自己,自己给别人的值是原来的值: 前置自操作:先把自己改变,然后把改变后的值给别人. ...
- (转载)Java多线程返回值处理
一.概述 到目前为止,我们已经能够声明并使一个线程任务运行起来了.但是遇到一个问题:现在定义的任务都没有任何返回值,那么加入我们希望一个任务运行结束后告诉我一个结果,该结果表名任务执行成功或失败,此时 ...
- Software Testing, Lab 1
1.Install Junit(4.12), Hamcrest(1.3) with Eclipse 2.Install Eclemma with Eclipse 3.Write a java prog ...
- Centos 忘记root密码怎么办?
1 - 在启动grub菜单,选择编辑选项启动 2 - 按键盘e键,来进入编辑界面 3 - 找到Linux 16的那一行,将ro改为rw init=/sysroot/bin/sh 4 - 现在按下 Co ...
- <转>jmeter(二十)阶梯式加压测试
本博客转载自:http://www.cnblogs.com/imyalost/category/846346.html 个人感觉不错,对jmeter讲解非常详细,担心以后找不到了,所以转发出来,留着慢 ...