python可以调用c语言编写的底层代码吗_Python基础笔记系列十四:python无缝调用c程序...

本系列教程供个人学习笔记使用,如果您要浏览可能需要其它编程语言基础(如C语言),why?因为我写得烂啊,只有我自己看得懂!!

python语言可以对c程序代码进行调用,以弥补python语言低性能的缺点。当然,它也不是直接就可以调用,需要我们对c代码进行一些中间过程处理,其基本流程如下:

1.创建c程序功能代码

------------1.1创建.c源程序文件(py_test1.c)

------------1.2创建.h头文件(py_test1.h)

2.python类型适配,包装c代码(写包裹文件)(py_test1wrapper.c)

------------2.1.包含Python.h头文件(在python安装目录下的include目录下找到)

------------2.2.为每一个函数设置一个PyObject *Module_func()的包裹函数

------------2.3.为模块增加一个PyMethodDef ModuleMethods[]的数组

------------2.4.增加模块的初始化函数void initModule()

3.编译和测试

-------------3.1编译安装到python环境

--------------------3.1.1)创建setup.py

--------------------3.1.2)运行setup.py编译和链接c的扩展代码

-------------3.2测试

--------------------3.2.1)从Python中导入模块

--------------------3.2.2)测试

创建c程序功能代码

一、创建.c源程序文件py_test1.c

这是程序的具体功能代码,也就是python需要调用的c源程序。这里主要写了三个方法,最终我们就会实现python来调用这三个方法。

ContractedBlock.gif

ExpandedBlockStart.gif

1 #include2 #include3 #include4

5

6 //求阶乘7 int fac(int n) {8 if(n < 2)9 return 1;10

11 return n*fac(n-1);12 }13

14

15

16 //字符串逆序17 char *reverse(char *s) {18 //比如输入abcdefg,则返回gfedcba19 char t,*p = s ,*q = (s+strlen(s)-1);20

21 while(s && (p

27 return s;28 }29

30

31 int test(void) //测试main方法,改成普通的test方法32 // int main(void)33 {34 char s[1024];35

36 printf("5! = %d\n",fac(5));//5的阶乘

37

38 printf("10! = %d\n",fac(10));// 10的阶乘

39

40

41 strcpy(s,"hello world");42 printf("reversing 'hello world',we get '%s'\n",reverse(s));43

44 return 0;45 }

c源程序

二、创建.h头文件py_test1.h

接下来写一个就像stdio.h这样的头文件,方面后面引用。这个文件里面主要就是声明了py_test1.c中的三个方法。

1 #ifndef PYTEST1_H_2 #define PYTEST1_H_

3

4 int fac (intn) ;5 char *reverse(char *s) ;6 int test(void) ;7

8 #endif

写包裹文件py_test1wrapper.c

这个包裹文件其实也是一段c语言代码,只是这一段代码比较特殊,它需要除c语言外还有一定的规则来创建它,这些规则就是python调用c定义的,必须遵循。

1.必须include包含Python.h这个头文件,可以说这里就慢慢地去靠向python了。这个头文件在python安装目录下的include目录下找到它,但我们并不需要去知道它在哪儿。

2.必须include包含py_test1.h这个头文件,这个就是上一步我们创建的那个头文件。

3.还记得我们的py_test1.c这个源程序文件吗?现在我们需要对它里面的每个方法(这里是三个)都要设置一个包裹函数,它必须以PyObject为返回值,并且每个函数都有两个必须的参数。

4.还要在这个文件里添加一个模块数组,类型必须是PyMethodDef,它用来定义方法名以及方法名与包裹函数的对应关系。

5.最后还要添加一个模块的初始化函数void initModule(),这里的函数名必须以init和模块名组成。

下面就是这个包裹文件的完整代码:

ContractedBlock.gif

ExpandedBlockStart.gif

1 #include "Python.h"

2 #include

3 #include

4 #include "py_test1.h"

5 /**6

7 **包裹文件8

9 **/

10

11 //为fac函数设置包裹函数(函数名、参数都有一定的规则,要注意)

12 static PyObject *py_test1_fac(PyObject *self,PyObject *args)13 {14

15 intnum ;16 //将python的数据类型int args通过i的方式转换成能被c识别的类型int num17 //i:表示将python的整型转成c的整型 ,其它类型可百度

18 if (!PyArg_ParseTuple(args,"i",&num))19 returnNULL;20

21

22 //调用c的对应函数并得到返回值,23 //然后将返回值c的数据类型int通过i的方式转换成能被python识别的类型int24 //最后强转成PyObject类型

25 return (PyObject *)Py_BuildValue("i",fac(num));26

27 }28

29

30 //为reverse函数设置包裹函数(由于python中有reverse函数,不能使用)

31 static PyObject *py_test1_doppel(PyObject *self,PyObject *args)32 {33 char *src;34 char *mstr;35 PyObject *retval;36

37 //s:python中str ----->C中char *

38 if (!PyArg_ParseTuple(args,"s",&src))39 returnNULL;40

41 //申请存储空间

42 mstr = malloc(strlen(src) +1);43 //拷贝src到mstr

44 strcpy(mstr,src);45 //调用reverse方法,逆序字符串

46 reverse(mstr);47 //这里把原字符串和转换后的字符串返回

48 retval = (PyObject *) Py_BuildValue("ss",src,mstr);49 //释放空间

50 free(mstr);51

52 returnretval;53 }54

55

56 //为test函数设置包裹函数

57 static PyObject *py_test1_test(PyObject *self,PyObject *args)58 {59 //直接调用c函数

60 test();61

62 return (PyObject *)Py_BuildValue("");63 }64

65

66 //添加模块数组(注意是PyMethodDef,不要错写成PyMethondDef)67 //定义对应的方法名,后面Python调用的时候就用这里面的方法名调用

68 static PyMethodDef py_test1Methods[] ={69 {"fac",py_test1_fac,METH_VARARGS},70 {"doppel",py_test1_doppel,METH_VARARGS},71 {"test",py_test1_test,METH_VARARGS},72 {NULL,NULL},73 };74

75

76 //模块初始化函数

77 void initpy_test1(void)78 {79 Py_InitModule("py_test1",py_test1Methods);80 }

包裹文件

到此为止,准备工作可以说已经完成了,接下来就需要编译、安装上面的那些文件了,怎么编译呢?

编译、安装和测试

一、编译安装

1.创建setup.py文件,文件内容也很简单,主要功能就是使用python的自带模块,将包裹文件编译。

1 #incoding:utf-8

2 from distutils.core importsetup,Extension3 #模块名

4 MOD = 'py_test1'

5 #资源(要编译和链接的代码文件)

6 source = ['py_test1.c','py_test1wrapper.c']7

8 #调用setup函数,编译和链接

9 setup(name=MOD,ext_modules=[Extension(MOD,sources=source)])

那如果运行这个setup.py 程序呢?这里就不能直接build了,需要到命令行里操作,定位要当前目录后通过命令python setup.py build来编译setup.py文件(出现各种错误,如:error: Unable to find vcvarsall.bat请看文章最后)

945531-20180505172136973-2030403121.png

2.编译完成过后,可以在当前文件目录下找到一个build文件夹,里面就是编译过后的内容了,我们也无需知道里面到底是些什么文件。编译完成我们怎么使用?别急,我们还要安装它们到python的库中,同样通过命令python setup.py install安装。

945531-20180505172801147-1437615784.png

这里其实就可以发现其实是往我们本地python库中安装了文件,也就是一个模块。在python安装目录下Lib目录的site-packages文件夹下。现在我们就可以使用它了。

二、测试

现在就可以使用python来调用最开始写的那个c程序了,别忘了要导入模块哦~

测试程序test.py:

1 #incoding:utf-8

2 importpy_test13 print help(py_test1) #查看里面都要哪些方法

4 py_test1.test() #调用test函数

5 print "-"*50

6 print py_test1.fac(9) #调用fac()求阶乘的函数

7 print py_test1.doppel("yycsetup") #调用逆序函数

运行输出:

1 Help on module py_test1:2

3 NAME4 py_test15

6 FILE7 d:\python27\lib\site-packages\py_test1.pyd8

9 FUNCTIONS10 doppel(...)11

12 fac(...)13

14 test(...)15

16

17 None18 5! = 120

19 10! = 3628800

20 reversing 'hello world',we get 'dlrow olleh'21 --------------------------------------------------22 362880

23 ('yycsetup','putescyy')24 [Finished in 0.5s]

错误解决

错误提示error: Unable to find vcvarsall.bat

解决地址 : 其实这个错就是缺少文件vcvarsall.bat,基本解决思路就是安装VCForPython27(安装过后可能提示找不到vc),修改python源代码(修改方法返回值)。下面这几个地址值得参考,标红的为重要解决思路。

------------https://www.cnblogs.com/yyds/p/7065637.html

------------安装VCForPython27:https://www.microsoft.com/en-us/download/details.aspx?id=44266

------------升级setuptools:https://pypi.org/project/setuptools/

------------设置setuptools环境变量:https://www.cnblogs.com/fbwfbi/p/4509622.html

------------修改方法返回值:https://www.cnblogs.com/lazyboy/p/4017567.html

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值