微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

python 调用c程序--pyobject实现

python调用c程序,通过动态链接库的方法比较麻烦,需要各种转换。另外一种比较直接的方法是使用pyobject扩展实现。
本文参考了https://www.cnblogs.com/hyyq/p/8995372.html

一、原理

方法的步骤如下:

  1. 编写c程序。此处与正常的c程序是一样的。
  2. 编写封装程序。将步骤1的c程序进行封装,主要是参数的封装。因为python的变量是无法直接被c语言调用的,需要进行转换才行。
  3. 初始化。初始化的功能相当于注册了步骤1的函数
  4. 编译
  5. 安装
  6. 通过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 举报,一经查实,本站将立刻删除。

相关推荐