Python2~3的C扩展

1 篇文章 0 订阅

为何要编写C扩展Python:

python语言的缺点

很多时候不能将程序连写成一行,如import sys;for i in sys.path:print i。而perl和awk就无此限制,可以较为方便的在shell下完成简单程序,不需要如Python一样,必须将程序写入一个.py文件。(对很多用户而言这也不算是限制)

(1)运行速度,有速度要求的话,用C++改写关键部分吧。不过对于用户而言,机器上运行速度是可以忽略的。因为用户根本感觉不出来这种速度的差异。

(2)既是优点也是缺点,python的开源性是的Python语言不能加密,但是目前国内市场纯粹靠编写软件卖给客户的越来越少,网站和移动应用不需要给客户源代码,所以这个问题就是问题了。国随着时间的推移,很多国内软件公司,尤其是游戏公司,也开始规模使用他。

(3) 构架选择太多(没有像C#这样的官方.net构架,也没有像ruby由于历史较短,构架开发的相对集中。Ruby on Rails 构架开发中小型web程序天下无敌)。不过这也从另一个侧面说明,python比较优秀,吸引的人才多,项目也多。

利用C扩展Python的好处:

  • C语言编译后变成机器,相对于明文的Python,可以有效的保护核心代码
  • Python运行效率慢,C执行效率高,可以有效解决性能瓶颈
  • C语言可以编写很多功能,可以在Python中创建C一些特有的东西

环境

Ubuntu16.04
Python2.7~Python3.5

Python2.7编写C扩展

(Ubuntu16.04 + VS2015 + python2.7)
C代码中在实现两个功能:

  • 求一个整数的阶乘

  • 求一个字符串的逆序
    提供test函数接口供调用,模块名设置为Extest
    以下代码是Extest.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)
      {
      	register char t,
      		*p = s,
      		*q = (s + (strlen(s) - 1));
    
      while (s && (p < q))
      {
      	t = *p;
      	*p++ = *q;
      	*q-- = t;
      }
      return(s);
      }
      
      int test()
      {
      	char s[BUFSIZ];
      	printf("4! == %d\n", fac(4));
      	printf("8! == %d\n", fac(8));
      	printf("12! == %d\n", fac(12));
      	strcpy(s, "abcdef");
      	printf("reversing 'abcdef', we get '%s'\n", reverse(s));
      	strcpy(s, "madam");
      	printf("reversing 'madam', we get '%s'\n", reverse(s));
      	return 0;
      }
    
      #include "Python.h"
      
      static PyObject *
      Extest_fac(PyObject *self, PyObject *args)
      {
      	int num;
      	if (!PyArg_ParseTuple(args, "i", &num))
      		return NULL;
      	return (PyObject*)Py_BuildValue("i", fac(num));
      }
      
      static PyObject *
      Extest_doppel(PyObject *self, PyObject *args)
      {
      	char *orig_str;
      	char *dupe_str;
      	PyObject* retval;
      
      	if (!PyArg_ParseTuple(args, "s", &orig_str))
      		return NULL;
      	retval = (PyObject*)Py_BuildValue("ss", orig_str, dupe_str = reverse(strdup(orig_str)));
      	free(dupe_str);
      	return retval;
      }
      
      static PyObject *
      Extest_test(PyObject *self, PyObject *args)
      {
      	test();
      	return (PyObject*)Py_BuildValue("");
      }
      
      static PyMethodDef
      ExtestMethods[] =
      {
      	{ "fac", Extest_fac, METH_VARARGS },
      	{ "doppel", Extest_doppel, METH_VARARGS },
      	{ "test", Extest_test, METH_VARARGS },
      	{ NULL, NULL },
      };
      
      void initExtest()
      {
      	Py_InitModule("Extest", ExtestMethods);
      }
    

以下代码是test-setup.py

#! /usr/bin/env python

from distutils.core import setup, Extension

MOD = 'Extest'
setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest.c'])])

运行方法:
【在python2.7环境下执行】
python test-setup.py build # 命令行执行这句会在当前目录下创建build文件夹,
cd build/lib.linux-x86_64-2.7/
进入python【python2.7】交互环境>>>
import Extest
Extest.test()
执行效果:
Python2执行效果
这样,你就能把这个生成的.so文件放入你的项目中,直接import Extest就能导入这个模块了.

Python3.5.2编写C扩展

(Ubuntu16.04 + VS2015 + python3.5.2)
C代码中在实现三个功能:

  • 求一个数的绝对值
  • 求一个字符串的逆序
  • 打印输出测试
    提供上述三个方法函数接口供调用,模块名设置为Extest

以下代码是my_extend.c:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<Python.h>

int my_abs(int n) {
	if (n<0)
		n = n * -1;
	return n;
}

void my_reverse(char *s) {
	if (s) {
		int len = strlen(s);
		int i;
		char t;
		for (i = 0;i<(len - 1) / 2;i++) {
			t = s[i];
			s[i] = s[len - 1 - i];
			s[len - 1 - i] = t;
		}
	}

}

void test(void) {
	printf("test my_abs:\n");
	printf("|-8590|=%d\n", my_abs(-8590));
	printf("|-0|=%d\n", my_abs(-0));
	printf("|5690|=%d\n", my_abs(-5690));

	printf("test my_reverse:\n");
	char s0[10] = "apple";
	char s1[20] = "I love you!";
	char *s2 = NULL;
	my_reverse(s0);
	my_reverse(s1);
	my_reverse(s2);
	printf("'apple' reverse is '%s'\n", s0);
	printf("'I love you!' reverse is '%s'\n", s1);
	printf("null reverse is %s\n", s2);
}

//作用,接受python传的值,将结果计算后转为Python对象返回给python
//返回类型PyObject*,函数名:模块名_函数名
static PyObject *Extest_abs(PyObject *self, PyObject *args) {
	int num;
	if (!(PyArg_ParseTuple(args, "i", &num))) {
		return NULL;
	}
	return (PyObject*)Py_BuildValue("i", my_abs(num));
}

static PyObject *Extest_reverse(PyObject *self, PyObject *args) {
	char *s;
	if (!(PyArg_ParseTuple(args, "z", &s))) {
		return NULL;
	}
	my_reverse(s);
	return (PyObject*)Py_BuildValue("s", s);
}

static PyObject *Extest_test(PyObject *self, PyObject *args) {
	test();
	return (PyObject*)Py_BuildValue("");
}

//为每个模块增加PyMethodDef ModuleMethods[]数组
static PyMethodDef ExtestMethods[] = {
	{ "abs",Extest_abs,METH_VARARGS },
	{ "reverse",Extest_reverse,METH_VARARGS },
	{ "test",Extest_test,METH_VARARGS },
	{ NULL,NULL },
};

static struct PyModuleDef ExtestModule = {
	PyModuleDef_HEAD_INIT,
	"Extest",
	NULL,
	-1,
	ExtestMethods
};

void PyInit_Extest() {
	PyModule_Create(&ExtestModule);
}

以下代码是test-setup1.py

#! /usr/bin/env python
from distutils.core import setup, Extension


def main():

    MOD = 'Extest'  # 模块名
    setup(name=MOD, ext_modules=[Extension(MOD, sources=['my_extend.c'])])  # 源文件名


if __name__ == '__main__':
    main()

运行方法:
【在python3.5.2环境下执行】
python test-setup1.py build # 命令行执行这句会在当前目录下创建build文件夹,
cd build/lib.linux-x86_64-3.6/
进入python【python3.5.2】交互环境>>>
import Extest
Extest.test()
Extest.abs(-6)
Extest.reverse(“abcdef”)

执行效果:
python3.5.2执行效果

附录:
python和c对应的类型转换参数表
Table 22.2 Common Codes to Convert Data Between Python and C/C++

Format CodePython TypeC/C++ Type
sstrchar*
zstr/Nonechar*/NULL
iintint
llonglong
cstrchar
dfloatdouble
DcomplexPy_Complex*
O(any)PyObject*
SstrPyStringObject

Py_BuildValue的用法表

Py_BuildValue("")None
Py_BuildValue(“i”, 123)123
Py_BuildValue(“iii”, 123, 456, 789)(123, 456, 789)
Py_BuildValue(“s”, “hello”)‘hello’
Py_BuildValue(“ss”, “hello”, “world”)(‘hello’, ‘world’)
Py_BuildValue(“s#”, “hello”, 4)‘hell’
Py_BuildValue("()")()
Py_BuildValue("(i)", 123)(123,)
Py_BuildValue("(ii)", 123, 456)(123, 456)
Py_BuildValue("(i,i)",123, 456)(123, 456)
Py_BuildValue("[i, i]",123,456)[123, 456]
Py_BuildValue("{s:i,s:i}", “abc”, 123 “def”, 456){‘abc’:123, “def”:456}
Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6)((1,2),(3,4)),(5,6)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值