扩展Python模块系列(二)—-一个简单的例子

扩展Python模块系列(二)—-一个简单的例子

大家好,又见面了,我是全栈君。

 

本节使用一个简单的例子引出Python C/C++ API的详细使用方法。针对的是CPython的解释器。

 目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两个点之间的距离。

可以在Python代码这样使用test模块:

import test
s = test.distance((0, 0, 0), (1, 1, 1))

先上代码:

[test.c]

#include <Python.h>
#include <math.h>

static PyObject* distance(PyObject* self, PyObject* args)
{
    double x0, y0, z0, x1, y1, z1;

    if (!PyArg_ParseTuple(args, "(ddd)(ddd)", &x0, &y0, &z0, &x1, &y1, &z1))
    {
        return NULL;
    }
    return PyFloat_FromDouble(sqrt((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1) + (z0 - z1) * (z0 - z1)));
}

static PyMethodDef cformula_methods[] =
{    
    { "distance", distance, METH_VARARGS, "Return the 2D Distance of 2 Points." },
    { NULL, NULL, 0, NULL },
};

PyMODINIT_FUNC inittest(void)
{
    Py_InitModule3("test", cformula_methods, "Common test Written in C.");
}

 [Source.cpp]

#include <Python.h>

PyMODINIT_FUNC inittest();

int main()
{
    //PyImport_AppendInittab("test", inittest);
    Py_Initialize(); 
    inittest();    
    PyRun_SimpleString("import test");
    PyRun_SimpleString("s = test.distance((0, 0, 0), (1, 1, 1))");
    PyRun_SimpleString("print s");
    return 0;
}

编译运行后,结果如下:

扩展Python模块系列(二)----一个简单的例子

如果希望将test模块打包为一个动态链接库,供其他Python程序使用,即打包为test.pyd,在其他Python程序中可以直接import test,就和正常的Python内建模块一样。

步骤如下:

1) 在test.c同级目录下,新建一个python文件setup.py

2)setup.py:

from distutils.core import setup, Extension 
setup(ext_modules=[Extension('test', sources = ['test.c'])])

3) 执行python命令: python setup.py build

扩展Python模块系列(二)----一个简单的例子

 这种情况是因为没有指定C的编译器,本文使用VS2015提供的编译器,所以首先执行:SET VS90COMNTOOLS=%VS140COMNTOOLS%

扩展Python模块系列(二)----一个简单的例子

会在该目录下发现build/lib.win32-2.7中有一个test.pyd,这就是编译后的动态库,可以直接import

扩展Python模块系列(二)----一个简单的例子

 

 实现细节:

1. 任何扩展Python模块的C程序,一般只需要包含<Python.h>头文件即可,文档中这样描述:

All function, type and macro definitions needed to use the Python/C API are included in your code by the following line:

#include “Python.h”

在包含了Python.h之后,隐含地会自动包含C语言的标准头文件<stdio.h>, <string.h>, <errno.h>, <limits.h>, <assert.h> and <stdlib.h>

2. 本质上Python C API提供了对C函数的wrapper。假设有一个现成的C函数, int add(int a, int b), 想把它实现在Python的模块里,需要实现一个wrapper函数 static PyObject* addAB(PyObject* self, PyObject* args), 将args解析为两个整数a、b,然后调用add(a,b),将返回值打包为一个Python整型对象返回。

 

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

 

函数一定要声明为static,将其限定在此文件范围;参数self是Python内部使用的,遵循范式即可;参数args是函数的参数列表,是一个tuple。

PyArg_ParseTuple(args, "(ddd)(ddd)", &x0, &y0, &z0, &x1, &y1, &z1)

此函数将参数列表args解析为两个tuple, 每个tuple是三个double类型的元素。如果参数列表不符合”(ddd)(ddd)”这种形式,直接返回NULL。

PyFloat_FromDouble

此函数将一个C原生的double,封装Python 的PyFloatObject。

3. 定义该模块对外提供的函数接口

static PyMethodDef cformula_methods[]

在此数据结构中定义模块test对外提供的函数接口,第一个参数”distance”是Python内部记录的,就是test.distance调用时,python查找的函数名;第二个参数distance是函数具体实现,本质是一个函数指针;第三个参数是METH_VARARGS告诉Python此函数一个典型的PyCFunction,参数为两个PyOBject*,参数2使用PyArg_ParseTuple来解析;最后一个是函数说明。

4.定义模块初始化函数

PyMODINIT_FUNC inittest(void)
{
    Py_InitModule3("test", cformula_methods, "Common test Written in C.");
}

函数inittest必须这样定义,如果模块名为example,那么模块初始化函数为initexample。宏定义PyMODINIT_FUNC定义如下:

#       if defined(__cplusplus)
#               define PyMODINIT_FUNC extern "C" void
#       else /* __cplusplus */
#               define PyMODINIT_FUNC void
#       endif /* __cplusplus */

针对cpp的实现加了修饰extern “C”。

Py_InitModule3创建模块test, 当import test时,Python解释器会找到inittest创建的模块test。

5. 测试代码Source.cpp

在Python虚拟机环境初始化 Py_Initialize()之后,调用inittest(),则会创建新模块test。

如果希望在import test时才初始化模块test,那么在Py_Initialize()之前调用PyImport_AppendInittab(“test”, inittest); 然后再注释掉initest()。

PyImport_AppendInittab:Add a single module to the existing table of built-in modules.The new module can be imported by the namename, and uses the function initfunc as the initialization function called on the first attempted import. This should be called before Py_Initialize().

 

本节通过一个简单的例子来展示了如何用C语言扩展Python模块,并简单解释了用到的Python C/C++ API。在下一节中,将更加深入地了解Python C/C++ API,以及在写扩展程序时,会遇到的坑,比如:异常处理、引用计数等等。

 

转载于:https://www.cnblogs.com/jianmu/p/7345716.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108244.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • eric6教程(电脑的配置基本知识)

    欢迎加入QQ群:853840665,一块学习讨论单击菜单栏Setting-&gt;Preferences,进入设置界面 下面从上而下设置比较常用的设置,其他的各位可以自行研究1.设置编译器2.设置API3设置自动补全4.设置自动检查时间间隔默认是5S,我在用的时候发现经常代码该正确了,却还是提示错误,这里把时间改短5.设置字体大小…

    2022年4月15日
    36
  • git gui提交无法获知你的身份 20

    git gui提交无法获知你的身份 20

    2021年10月9日
    66
  • 服务器中了挖矿病毒怎么办

    服务器中了挖矿病毒怎么办前言服务器好端端的竟然中了挖矿病毒!!!可怜我那1核2G的服务器,又弱又小,却还免除不了被拉去当矿工的命运,实在是惨啊惨。事情原来是这样的。。。就在今天下午,我准备登陆自己的远程服务器搞点东西的时候,突然发现ssh登陆不上了。如上,提示被拒绝。这个问题很明显就是服务器没有我的公钥,或者不识别我的公钥,然后拒绝登录。这就很难办了,我确定我的公钥是一直没有变动过的,不应该会出现这种情况啊。还有让我头疼的是,我当初为了安全起见,设置过此台服务器只能通过ssh的方式

    2022年6月3日
    102
  • Windows 10 本地 IIS Web服务器搭建

    Windows 10 本地 IIS Web服务器搭建IIS服务器搭建启用功能①右击我的电脑点击属性,进入windows10控制面板。然后点击控制面板②点击进入程序和功能,然后点击启用或关闭windows功能,打开windows功能窗口,选择InternetInformationServices选项,将里面的内容全部勾选。如图:③勾选完成后,点击确定按钮,windows自动安装IIS功能,完成…

    2022年5月11日
    48
  • 泛域名解析

    泛域名解析

    2021年9月2日
    54
  • JavaScript(1)高阶函数filter、map、reduce

    JavaScript(1)高阶函数filter、map、reduce前言需求:有这样一个数组[10,20,110,200,60,30,40]1.筛选出数组中小于100的元素2.将筛选出的每个元素的值x23.完成第2步之后,将数组中的所有元素加起来

    2022年7月29日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号