C语言编写Python包,使用C语言编写Python扩展1——Hello World

能够使用C语言编写扩展是Python一大卖点吧,这可以将一些关键的代码使用C来写以提升程序的性能。本文是参考了Python的官方文档整理而来的,同时结合了Python2跟Python3。按照惯例现在先从一个Hello World开始讲解一下写扩展的基本流程。

详细的内容可以参考官方文档:

https://docs.python.org/2.7/extending/index.html

https://docs.python.org/3/extending/index.html

https://docs.python.org/2.7/c-api/index.html

https://docs.python.org/3/c-api/index.html

同时本文中的示例代码可从 https://github.com/wusuopu/python-c-extension-sample 获取到。

首先介绍一下我当前的开发环境:

* ArchLinux

* gcc 4.8.2

* glibc 2.19

* Python 2.7.6

* Python 3.3.5

开始

先创建一个新的C代码文件 lc_hello.c。为了能够正常使用python的api,需要导入Python.h这个头文件。

#include

然后再定义一个模块的初始化函数。

PyMODINIT_FUNC initlc_hello_world(void)

{

Py_InitModule("lc_hello_world", lc_hello_world_methods);

printf("init lc_hello_world module\n");

}

这个函数是用于模块初始化的,即是在第一次使用import语句导入模块时会执行。其函数名必须为initmodule_name这样的格式,在这里我们的模块名为lc_hello_world,所以函数名就是initlc_hello_world。

在这个函数中又调用了Py_InitModule函数,它执行了模块初始化的操作。Py_InitModule函数传入了两个参数,第一个参数为字符串,表示模块的名称;第二个参数是一个PyMethodDef的结构体数组,表示该模块都具有哪些方法。与Py_InitModule相似的方法还有Py_InitModule3和Py_InitModule4。因此在initlc_hello_world方法之前还需要先定义 lc_hello_world_methods 数组。

static PyMethodDef lc_hello_world_methods[] = {

{"test", (PyCFunction)test_function, METH_NOARGS, "lc_hello_world extending test"},

{"add", (PyCFunction)add_function, METH_VARARGS, NULL},

{NULL, NULL, 0, NULL}

};

PyMethodDef结构体有四个字段。

* 第一个是一个字符串,表示在Python中对应的方法的名称;

* 第二个是对应的C代码的函数;

* 第三个是一个标致位,表示该Python方法是否需要参数,METH_NOARGS表示不需要参数,METH_VARARGS表示需要参数;

* 第四个是一个字符串,它是该方法的__doc__属性,这个不是必须的,可以为NULL。

PyMethodDef结构体数组最后以 {NULL, NULL, 0, NULL}结尾。(感觉好像不是必须的,但是通常都这么做那我们也这么做吧)

注意

:以上的用法都是针对Python2的,在Python3中又有些不同。

在Python3中模块的初始化函数的函数名变为了PyInit_module_name这样的形式了,因此这里就需要定义一个函数 PyMODINIT_FUNC PyInit_lc_hello_world。并且还需要返回一个 module 类型的变量。

其次在Python3中创建module对象的函数也由 Py_InitModule 变为了 PyModule_Create。

因此在Python3中模块的初始化函数应该定义如下:

PyMODINIT_FUNC PyInit_lc_hello_world(void)

{

PyObject *m;

m = PyModule_Create(&lc_hello_world_module);

if (m == NULL)

return NULL;

printf("init lc_hello_world module\n");

return m;

}

PyModule_Create函数需要传入一个 PyModuleDef 类型的指针。

因此在此之前还需要先定义 lc_hello_world_module 变量。

static struct PyModuleDef lc_hello_world_module = {

PyModuleDef_HEAD_INIT,

"lc_hello_world",/* name of module */

NULL,/* module documentation, may be NULL */

-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */

lc_hello_world_methods /* A pointer to a table of module-level functions, described by PyMethodDef values. Can be NULL if no functions are present. */

};

在 lc_hello_world_methods 中我们为模块指定了两个方法,接下来我们需要实现这两个方法。

static PyObject* test_function(PyObject *self)

{

PyObject_Print(self, stdout, 0);

printf("lc_hello_world test\n");

Py_INCREF(Py_True);

return Py_True;

}

这段代码定义了Python的test方法所对应的C函数。在这个函数中就只执行了一条printf语句,然后就返回了Py_True。

Py_True即是Python中的True,Py_INCREF函数执行的操作是对Python对象的计数引用值进行加1。与Py_INCREF对应的是Py_DECREF,它是对计数引用减1,并且计数引用为0时就销毁对象并回收内存。

static PyObject* add_function(PyObject *self, PyObject *args)

{

int num1, num2;

PyObject *result=NULL;

if (!PyArg_ParseTuple(args, "nn", &num1, &num2)) {

printf("传入参数错误!\n");

return NULL;

}

result = PyInt_FromLong(num1+num2);

return result;

}

这须代码定义了Python的add方法所对应的C函数。该函数需要传入两个整数类型的参数。

PyArg_ParseTuple是对传入的参数进行解析,关于这个函数的说明请查看Python手册。

注意

:在Python3中整数都是 long 类型的,因此这里的 PyInt_FromLong 需要改为 PyLong_FromLong,其作用是将C的int类型转为Python的int类型。

编译

扩展模块编写完成后,接下来就是对其进行编译了。先编写一个 setup.py 脚本。

#!/usr/bin/env python

#-*- coding:utf-8 -*-

from setuptools import setup, Extension

hello_world = Extension('lc_hello_world', sources=["lc_hello.c"])

setup(ext_modules=[hello_world])

然后再执行命令进行编译:

$ python setup.py build

执行成功后会在当前目录下的build目录中生成扩展模块文件。

测试

最后就是编写一个小程序来测试刚刚的模块是否可用。

import lc_hello_world

print(lc_hello_world.test.__doc__)

print(lc_hello_world.add.__doc__)

print(lc_hello_world.test())

print(lc_hello_world.add(1, 2))

print(lc_hello_world.add(1, '2')) # 这个会报错

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值