c调用python获取返回值_Python 通过 ctypes 调用 C 程序实例

原标题:Python 通过 ctypes 调用 C 程序实例

来源:思诚之道

http://www.bjhee.com/python-ctypes.html

在做复杂计算时,Python的执行效率是无法同C比的。而且有些算法已经有开源的C库了,我们也没必要用Python重写一份。那Python怎么调用C语言写的程序库呢?答案就在Python提供的ctypes库,它提供同C语言兼容的数据类型,可以很方便地调用C语言动态链接库中的函数。

使用C标准库函数

我们来试试调用C标准库函数:

fromctypesimportcdll

libc=cdll.LoadLibrary('libc.so.6')# Load standard C library on Linux

# libc = cdll.LoadLibrary('libc.dylib') # Load standard C library on Mac

# libc = cdll.msvcrt # Load standard C library on Windows

printlibc.time(None)

上述代码加载了Linux中的C标准库”libc.so.6″,并调用其中”time()”函数,执行后屏幕上会打印出当前时间戳。注,Windows和Mac上的加载方法在注释中。

调用自定义的动态链接库

我们先根据这篇文章(http://www.bjhee.com/java-native-1.html)写个动态链接库,现在你有了库”libhello.so”,其有一个hello函数。让我们在Python中调用它:

fromctypesimportcdll

libhello=cdll.LoadLibrary("./libhello.so")

libhello.hello('You')

看到屏幕上”Hello You!”的字样了吧。对!就这么简单,比起Java调用本地代码方便很多吧。注意,本例中的”libhello.so”同Python程序在同一目录下。

效率对比

我们写个阶乘(factorial)函数,来比较Python和C的执行效率吧。先写C代码:

intfactorial(intn)

{

if(n<2)

return1;

returnfactorial(n-1)*n;

}

方便起见,我们把它放在之前写的”hello.c”文件中,这样就可以从”libhello.so”中调用它。别忘了在”hello.h”中声明这个函数。然后实现Python代码:

deffactorial(n):

ifn<2:

return1

returnfactorial(n-1)*n

deffactorial_c(n):

returnlibhello.factorial(n)

Python的实现可以说同C的一模一样,我们另外定义一个”factorial_c()”函数来封装C的调用。现在,我们来比较下执行效率。这里要引入Python的”timeit”包,它可以帮你计算程序的执行时间,省去你很多代码。让我们来算20的阶乘,并计算10万次,看看所消耗的时间:

fromtimeitimporttimeit

f_setup='from __main__ import factorial'

f_c_setup='from __main__ import factorial_c'

printtimeit('factorial(20)',setup=f_setup,number=100000)

printtimeit('factorial_c(20)',setup=f_c_setup,number=100000)

我在虚拟机上跑的结果结果是:

0.231598138809

0.0475780963898

差不多5倍的差距啊!

参数传址

大家知道C的函数参数是传值的(其实Python也一样),那我想在C中改变参数的值怎么办,那就需要传递引用了。我们在上面的”libhello.so”中加一个快排函数:

void quickSort(int*a,intstart,intend)

{

if(start

{

intleft=start;

intright=end;

intkey=a[left];

while(left

{

while(left=key)

right--;

a[left]=a[right];

while(left

left++;

a[right]=a[left];

}

a[left]=key;

quickSort(a,start,right-1);

quickSort(a,left+1,end);

}

}

朋友们马上可以看出,这段函数中数组a中的值是可以被改变的。那Python怎么调用它呢?就是在参数传递时,加上”byref()”调用,它是ctypes提供的方法,如果用它调用int型变量a时,作用类似于”(int *) &a”。所以我们的Python程序可以这样写:

fromctypesimportcdll,c_int,byref

defquick_sort(numbers):

size=len(numbers)

c_numbers=(c_int*size)(*numbers)

libhello.quickSort(byref(c_numbers),0,size)

returnc_numbers

这里还有个知识点,就是C类型。为了同C的变量类型兼容,ctypes库提供了一系列对应的C类型。本例中c_int就是对应C中的int型。我们将”c_int * 10″就等于创建一个长度为10的int型数组。而后面的(*number)就是把numbers的值赋给刚创建的int数组。ctypes库所有提供的C类型可以在这里查到。

上例中,我们必须传入C类型的整型数组才能被C程序接收。现在让我们来使用下这个快排:

fromrandomimportshuffle,sample

numbers=sample(range(1000),99)

shuffle(numbers)

sorted_num=quick_sort(numbers)

foriinsorted_num:

printi

有兴致的朋友们也可以写个Python的快排来比较下效率。

参数及返回类型指定

我们回到C标准库,调用下”strchr”方法,它的作用是在字符串中找出以指定字符开头的子串。

strchr=libc.strchr

printstrchr('abcdef','d')

你会发现,返回一直是0,而我们期望的应该是”def”。其实,问题是在我们的第二个参数,它应该是一个字符,而Python中它是一个字符串。那怎么让它成为字符类型呢?一个方法是使用”strchr(‘abcdef’, ord(‘d’))”调用”strchr”方法,”ord()”函数可以把字符串变成字符类型,但是每次调用都要加上,很麻烦。还有一个办法就是指定函数输入参数的类型。我们可以加上代码:

fromctypesimportc_char,c_char_p

strchr=libc.strchr

strchr.argtypes=[c_char_p,c_char]

printstrchr('abcdef','d')

函数的”argtypes”属性就可以指定传入参数的类型。这里,第一个参数是字符指针,也就是C中的字符串,第二个是字符。

我们再来执行下程序,奇怪,虽然有返回了,但一直是一个长整型数值,为什么呢?了解’strchr’的朋友们应该知道,这个函数返回的是”char *”类型,它是一个字符指针,所以你在Python中获取的那个数值,就是指针的地址。那要怎么把指针转为字符串呢?也很简单,通过函数的”restype”属性指定返回值类型即可。完整的程序如下:

fromctypesimportcdll,c_char,c_char_p

libc=cdll.LoadLibrary('libc.so.6')# Load standard C library on Linux

strchr=libc.strchr

strchr.argtypes=[c_char_p,c_char]

strchr.restype=c_char_p

substr=strchr('abcdef','d')

ifsubstr:

printsubstr

关于ctypes库的更详细内容可以参考Python官方文档。

看完本文有收获?请转发分享给更多人

关注「Python开发者」,提升Python技能返回搜狐,查看更多

责任编辑:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值