python一个非常的大的优点就是开发效率高,非常不好的缺点就是执行效率低;然而c语言有个则刚好相反。还有一点python的对源码的保护做不到,即使你用py2exe,pyinstaller这样的方法也是很容易被反编译出来。但是c写的代码反编译的难度就极大地增加。所以如果你写的代码里面如果包含了一些敏感的东西,那么你可以把这段代码使用c来写。
那么我们是不是可以结合起来使用呢?
答案是完全可以,用c来写一个动态库/共享库,然后在python中调用。
python 的解释器本身就是用c写的,所以调用c是很方便的,调用的方法不止一种,我们这里介绍使用ctypes这种方式。
我们会写一个测试代码来演示怎么在python中调用c函数,包括python传整型和字符串参数给c库,c库中返回整型和字符串。这个测试将在windows xp平台+python2.7实施。
接下来我们把要做的事情分成2个阶段,用c编写一个动态库,在python中调用这个动态库。
c编写一个动态库
使用vc6创建一个空的win32动态库,添加2个文件
testcdll.c
tsetcdll.def
testcdll.c的内容如下:
#include <stdlib.h> #include <string.h> int add( int a, int b ) { return a + b; } char* pStr = 0; int nMax = 128; char* makestring( ) { pStr = ( char* )malloc(nMax); memset( pStr, 0, nMax ); strcpy( pStr, "test is test string" ); return pStr; } void delstring( void ) { if ( 0 != pStr ) { free( pStr ); pStr = 0; } } char* catstring( char* pinput ) { if ( 0 == pinput ) { return 0; } if ( 0 == pStr ) { pStr = ( char* )malloc(nMax); } memset( pStr, 0, nMax ); strcpy( pStr, "test is test string--" ); strcat( pStr, pinput ); return pStr; }
然后编译,得到后面测试要用到的testcdll.dll
如果你不想用IDE的话, 你可以直接使用命令行来编译:
cl /LD testcdll.c tsetcdll.def /link /out:testcdll.dll
调用这个动态库
因为我们的测试比较简单所以我们就不写源码文件,直接在解释器中写测试代码了。
打开命令行cmd,cd到刚才我们生成dll的文件夹,执行python
到解释器界面,依次执行下面的命令:
>>> from ctypes import * >>> hd = cdll.LoadLibrary('FoxLicenseMgr.dll') >>> hd.add( 2, 3 ) 5 >>> hd.makestring.restype = c_char_p >>> hd.makestring() 'test is test string' >>> hd.delstring() 1 >>> s1 = 'python' >>> cs1 = c_char_p( s1 ) >>> hd.catstring(cs1) 12068640 >>> hd.catstring.restype = c_char_p >>> hd.catstring(cs1) 'test is test string--python' >>> dir(hd) ['_FuncPtr', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '_ _getattr__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__modul e__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__si zeof__', '__str__', '__subclasshook__', '__weakref__', '_func_flags_', '_func_re stype_', '_handle', '_name', 'catstring', 'delstring', 'makestring'] >>> exit()
对上面语句稍作解释,
from ctypes import *
因为我们使用的是ctypes的这种方式,需要导入ctypes。
hd = cdll.LoadLibrary('testcdll.dll')
加载我们之前生成的动态库,因为我们已经cd到testcdll.dll所在的目录所以不用加全路径,否则你需要写全路径的。
hd.add( 2, 3 )
直接调用add函数,传进2个参数,参数不需要额外的转换,直接传进去,有些类型是不可以直接传的,需要做个转换,这个后面会提到。调用这一句后,就直接输入结果到控制台上结果为5
调用返回为字符串的函数的时候,需要设置返回类型,否则就会返回一整型值,这是python调用c函数的默认的返回值,如上面调用catstring的之前没有设置返回值,结果返回一个数值
>>> hd.catstring(cs1) 12068640
设置返回类型使用下面的语法:
hd.makestring.restype = c_char_p
因为我们的返回类型是char*, 所以使用c_char_p,这个和char*是对应的。其他的python的ctypes的类型和c类型的映射关系如下:
ctypes type | C type | Python type |
---|---|---|
c_bool | _Bool | bool (1) |
c_char | char | 1-character string |
c_wchar | wchar_t | 1-character unicode string |
c_byte | char | int/long |
c_ubyte |
unsigned char |
int/long |
c_short | short | int/long |
c_ushort |
unsigned short |
int/long |
c_int | int | int/long |
c_uint |
unsigned int |
int/long |
c_long | long | int/long |
c_ulong |
unsigned long |
int/long |
c_longlong | __int64 orlonglong | int/long |
c_ulonglong |
unsigned __int64 orunsignedlonglong |
int/long |
c_float | float | float |
c_double | double | float |
c_longdouble |
long double |
float |
c_char_p |
char * (NUL terminated) |
string or None |
c_wchar_p |
wchar_t * (NUL terminated) |
unicode or None |
c_void_p |
void * |
int/long or None |
调用makestring()来返回一个字符串:
>>> hd.makestring() 'test is test string'
如果传入的是字符串,也需要做一个转换
>>> s1 = 'python' >>> cs1 = c_char_p( s1 )
然后再传入,因为这个函数的返回的是字符串因此也需要设置返回类型:
>>> hd.catstring.restype = c_char_p >>> hd.catstring(cs1) 'test is test string--python'
最后使用dir来查看这个hd的都有那些可用的方法。
完。
版权所有,禁止转载. 如需转载,请先征得博主的同意,并且表明文章出处,否则按侵权处理.
One Reply to “python调用c”