python调用c程序,通过动态链接库的方法比较麻烦,需要各种转换。另外一种比较直接的方法是使用pyobject扩展实现。
本文参考了https://www.cnblogs.com/hyyq/p/8995372.html。
python调用c程序
一、原理
此方法的步骤如下:
- 编写c程序。此处与正常的c程序是一样的。
- 编写封装程序。将步骤1的c程序进行封装,主要是参数的封装。因为python的变量是无法直接被c语言调用的,需要进行转换才行。
- 初始化。初始化的功能相当于注册了步骤1的函数。
- 编译
- 安装
- 通过import导入模块,进行调用
二、实现
1. 编写c程序
保留了参考链接中的3个函数,并扩展了字符查找功能。代码如下:
//py_test.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int fac(int n) {
if(n < 2)
return 1;
return n*fac(n-1);
}
char *reverse(char *s) {
//比如输入abcdefg,则返回gfedcba
char t,*p = s ,*q = (s+strlen(s)-1);
while(s && (p<q)) {
t = *p;
*p++ = *q;
*q-- = t;
}
return s;
}
int test(void) //测试main方法,改成普通的test方法
{
char s[1024];
printf("5! = %d\n",fac(5)); //5的阶乘
printf("10! = %d\n",fac(10)); // 10的阶乘
strcpy(s,"hello world");
printf("reversing 'hello world',we get '%s'\n",reverse(s));
return 0;
}
char *find_chr(char *s, int c){
//int c:支持中文查找
return strchr(s,c);
}
头文件:
//py_test.h
#ifndef PYTEST_H_
#define PYTEST_H_
int fac (int n) ;
char *reverse(char *s) ;
int test(void) ;
char *find_chr(char *s,int c);
#endif
2. 编写封装程序
封装程序是实现python与c之间的参数转换,以及封装。
#include "Python.h"
#include <stdlib.h>
#include <string.h>
#include "py_test.h"
static PyObject *py_test_fac(PyObject *self,PyObject *args)
{
int num ;
//将python的数据类型int args通过i的方式转换成能被c识别的类型int num
//i:表示将python的整型转成c的整型 ,其它类型可百度
if (!PyArg_ParseTuple(args,"i",&num))
return NULL;
//调用c的对应函数并得到返回值,
//然后将返回值c的数据类型int通过i的方式转换成能被python识别的类型int
//最后强转成PyObject类型
return (PyObject *)Py_BuildValue("i",fac(num));
}
//为reverse函数设置包裹函数(由于python中有reverse函数,不能使用)
static PyObject *py_test_doppel(PyObject *self,PyObject *args)
{
char *src;
char *mstr;
PyObject *retval;
//s:python中str ----->C中char *
if (!PyArg_ParseTuple(args,"s",&src))
return NULL;
//申请存储空间
mstr = malloc(strlen(src) +1);
//拷贝src到mstr
strcpy(mstr,src);
//调用reverse方法,逆序字符串
reverse(mstr);
//这里把原字符串和转换后的字符串返回
retval = (PyObject *) Py_BuildValue("ss",src,mstr);
//释放空间
free(mstr);
return retval;
}
//为test函数设置包裹函数
static PyObject *py_test_test(PyObject *self,PyObject *args)
{
//直接调用c函数
test();
return (PyObject *)Py_BuildValue("");
}
static PyObject *py_test_find_chr(PyObject *self,PyObject *args)
{
char *src;
int c;
if (!PyArg_ParseTuple(args,"si",&src,&c))
return NULL;
return (PyObject *)Py_BuildValue("s",find_chr(src,c));
}
//添加模块数组(注意是PyMethodDef,不要错写成PyMethondDef)
//定义对应的方法名,后面Python调用的时候就用这里面的方法名调用
static PyMethodDef py_testMethods[] = {
{"fac",py_test_fac,METH_VaraRGS},
{"doppel",py_test_doppel,METH_VaraRGS},
{"test",py_test_test,METH_VaraRGS},
{"find_chr",py_test_find_chr,METH_VaraRGS},
{NULL,NULL},
};
static struct PyModuleDef py_test =
{
PyModuleDef_HEAD_INIT,
"py_test", /* name of module */
"usage: \n", /* module documentation, may be NULL */
-1,
py_testMethods
};
PyMODINIT_FUNC PyInit_py_test(void)
{
return PyModule_Create(&py_test);
}
原代码是基于python2.x的,最后的初始化部分已经废弃了,需要用新的初始化方法。
还有一点是当有多个参数时,需要先声明参数,然后检查:
char *src;
int c;
if (!PyArg_ParseTuple(args,"si",&src,&c))
return NULL;
”si“表示1个字符串,和1个整型数。可以参考下表英文说明:
python | C | 含义 | 备注 |
---|---|---|---|
s | char * | 字符串 | |
s# | [char *,int] | 字符串,长度 | |
z | char * | 字符串 | 类似s |
z# | char *,int | 字符串,长度 | 类似s# |
u | Py_UNICODE * | Unicode字符串 | |
u# | Py_UNICODE *,int | Unicode字符串,长度 | |
es | const char *encoding, char **buffer | ||
es# | const char *encoding, char **buffer,int | ||
b | char | 字符 | |
h | short int | 短整型 | |
i | int | 整型 | |
l | long int | 长整型 | |
c | char | 字符 | |
f | float | 浮点 | |
d | double | 双精度浮点 | |
D | Py_complex | 复数 | |
O | PyObject * | 对象 | |
O! | typeobject, PyObject * | ||
O& | converter, anything | ||
S | PyStringObject * | ||
U | PyUnicodeObject * | ||
t# | char *, int | 只读字符串,长度 | |
w | char * | 字符串 | 可读写 |
w# | char *,int | 字符串,长度 | 可读写 |
(items) | matching-items |
3. 定义方法
static PyMethodDef py_testMethods[] = {
{"fac",py_test_fac,METH_VaraRGS},
{"doppel",py_test_doppel,METH_VaraRGS},
{"test",py_test_test,METH_VaraRGS},
{"find_chr",py_test_find_chr,METH_VaraRGS},
{NULL,NULL},
};
4. 初始化
static struct PyModuleDef py_test =
{
PyModuleDef_HEAD_INIT,
"py_test", /* name of module */
"usage: \n", /* module documentation, may be NULL */
-1,
py_testMethods
};
PyMODINIT_FUNC PyInit_py_test(void)
{
return PyModule_Create(&py_test);
}
5. setup.py
#incoding:utf-8
from distutils.core import setup,Extension
#模块名
MOD = 'py_test'
#资源(要编译和链接的代码文件)
source = ['py_test.c','py_testw.c']
#调用setup函数,编译和链接
setup(name=MOD,ext_modules=[Extension(MOD,sources=source)])
6. include路径
python.h文件是在python安装目录的include文件夹下面的。如果使用的是cl编译器,需要设置INCLUDE环境变量和LIB环境变量。
7.编译
编译通过python setup.py build。需要注意生成是32位还是64位。一般是需要生成64位的。
8. 安装
安装通过python setup.py install实现
9.使用
使用代码如下:
#coding:utf8
import py_test
def main():
str = "uyythkhaskl;kdpoeirsdkl;f;lskfsdopfuisdl;kfsl;difkl;sdkfpeortisdkfl;sdkfsopi我rs;elkfpowierawkr;fliaskfopiskjfopawkpfokaspofp[skfk6786688768jhabcd"
a='我'.encode("utf8")
c = int.from_bytes(a, byteorder='little')
t = py_test.find_chr(str,c)
print("c 方法: ",t)
p = str.find('我')
print("python 方法:",str[p:])
t_eng = py_test.find_chr(str,ord("a"))
print("c 方法: ",t_eng)
p_eng = str.find('a')
print("python 方法:",str[p_eng:])
main()
查找中文稍微麻烦一些,需要转换为整型数。英文字符需要使用ord函数。
三、总结
python调用c函数在处理循环上要比python更快。但是对于查找等功能,使用python内置的方法反而更快速。估计是参数转换等耗费了时间导致的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。