Py_buffer

2023-10-26

使用C/C++扩展Python之一

Posted on Thu 12 November 2015 in Python

假设我们需要使用C/C++实现一个翻转字符串的扩展功能, 下面是C语言的实现

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

char* reverse(char *s)
{
    if (NULL == s) {
        return NULL;
    }
    int low = 0;
    int higth = strlen(s) - 1;
    while (low < hight) {
        char tmp = s[low];
        s[low] = s[hight]
        s[hight] = tmp;
        ++low;
        --hight;
    }
    return s;
}

首先,我们需要解决的问题是,怎样在Python环境下传递参数,即在Python环境下调用扩展模块,其传入的参数怎样被C扩展模块识别,再有就是C扩展模块识别后做了处理返回的数据怎样体现到Python环境下在调用该模块的返回值中去。Python提供了一系列的函数族(类似于PyArg_parse*形式的函数) 把Python环境传入的参数转化成C扩展模块能识别的类型,并提供了另一的函数Py_BuildValue解决C扩展模块返回值转换为Python环境能识别的数据类型。 这就是我们第一步需要做的:封装C扩展模块。

先来看看参数解析函数,这一系列函数常用的有3个,以下是这三个函数的定义:

int PyArg_ParseTuple(PyObject *args, const char *format, ...)
int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char *keywords[], ...) #这个主要实现Python的参数默认值和形如k=v的参数
int PyArg_Parse(PyObject *args, const char *format, ...) #不推荐使用

看着是不是和scanf函数有些形似,参数args必须是一个元组对象,其元素为从python环境传递到C模块的参数列表。 参数format必须是一个格式化字符串,剩下的参数必须传入的是地址, 其类型由格式化字符串决定, 格式化字符串的语法如下(捡几个常用的,完整的请看Python的官方手册):

字符 Python数据类型 C数据类型 含义
s string/Unicode const char* 适配一个参数:指向字符串的指针。需要注意的C程序不需要提供字符串的存储空间,且Python字符串不能包含'\0'字符, 否则会引发TypeError异常。Unicode字符若在转换成C字符失败,将抛出UnicodeError异常
s# string/Unicode/读缓冲区兼容的对象 const char*, int/Py_ssize_t 类似于格式化参数s。其适配两个参数:一个是指向字符串的指针,一个是int(或者Py_ssize_t)类型的整数。 其允许Python字符串中有'\0'字符。
s* string/Unicode/读缓冲区兼容的对象 Py_buffer 类似于格式化参数s#。
z string/Unicode/None const char* 类似于格式化参数s, 但是Python字符串可以为空,即适配的字符串指针指向为空
z# string/Unicode/None/读缓冲区兼容的对象 const char*, int/Py_ssize_t 类似于格式化参数s。其适配两个参数:一个是指向字符串的指针,一个是int(或者Py_ssize_t)类型的整数。 其允许Python字符串中有'\0'字符。
z* string/Unicode/None/读缓冲区兼容的对象 Py_buffer 类似于格式化参数s#。
i integer int  
I integer unsigned int 不带溢出检查
l integer long int  
k integer unsigned long int 不带溢出检查
\   - -
static PyObject* py_reverse(PyObject* self, PyObject* args)
{
    char* result;
    char* target = NULL;
    PyObject* retval = NULL;

    int ret = PyArg_parseTuple(args, "s", &target);
    if (!ret) {
        return NULL;
    }   

    result = reverse(target);
    retval = (PyObject*)Py_BuildValue("s", result);
    return retval;
}

第二步, 建立映射关系 如上面所说,通过结构体PyMethodDef来实现Python扩展模块中方法和C模块中方法的映射关系,让我们看看结构体PyMethodDef的定义:

// methodobject.h
struct PyMethodDef {
    const char  *ml_name; // Python扩展模块中的方法名称
    PyCFunction  ml_meth; // 第二步中我们封装的函数, 此处是函数地址 , 需要对其做PyCFunction类型的强制转换, 下面会说道
    int      ml_flags; // 代表这ml_meth不同的函数签名形式
    const char  *ml_doc; // 该Python扩展方法的文档 
};

在这里需要注意的是PyCFunction这个函数指针类型,其定义如下:

// methodobject.h
typedef PyObject *(*PyCFunction)(PyObject *, PyObject *)

这个PyCFunction类型是大多数封装C模块方法的签名形式,它会传入一个元组(也就参数列表中第二个参数, 第一个参数给自身用的),其包括所有的从Python环境传过来的参数, 这些参数需要通过PyArg_ParseTuple()或者PyArg_UnpackTuple()这两个函数来转换成C语言能识别的数据类型。 标示ml_flags的值为METH_VARARGS就代表这这种形式的函数签名。

其实PyCFunction下面还定义了两个封装C模块方法的函数签名形式:

// methodobject.h
typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, PyObject *);
typedef PyObject *(*PyNoArgsFunction)(PyObject *);

这两种会在后续讲到。

下面来完成映射:

static struct PyMethodDef reverse_methods[] = {
    {"reverse", (PyCFunction)py_reverse, METH_VARARGS, NULL},
    {NULL, NULL, 0, NULL},
};

第三步, 初始化模块 这里需要注意的是,函数名称格式必须为"init"+模块的名字, 且其返回值的类型是void, 函数的参数列表必须为void,具体原因可以看这里, 我们看看PyMONDINIT_FUNC的声明:

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

在Python环境下,首次导入扩展模块的时候,就会调用初始化函数, 其调用Py_InitModule3(下面代码)创建一个模块. 下面,来完成初始化工作

PyMODINIT_FUNC initreverse(void) {
    Py_InitModule3("reverse", reverse_methods, "My first extension module.");
}

第四步, 使用distutils来生成和安装模块 从python2.6起,可以使用distutils来生成和安装模块了,唯一需要做的是你的python环境里面安装了distutils模块,且只需要写一个简单的 setup.py脚本build和install扩展模块。

from distutils.core import setup, Extensiion

moduleReverse = Extension('reverse',
            sources = ['reverse.c'])
setup(name = 'reverse',
    version = '1.0',
    description = 'This is a test!',
    ext_modules = [moduleReverse])

然后执行 python setup.py build 和 python setup.py install 然后就可以使用了:

>>> import reverse
>>> dir(reverse)
['__doc__', '__file__', '__name__', '__package__', 'reverse']
>>> reverse.reverse('Zewen')   
neweZ

到此,一个简单的Python扩展模块就完成了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Py_buffer 的相关文章

随机推荐

  • mybatis- useSSL=true会报错

    D Java jdk1 8 0 144 bin java exe ea Didea test cyclic buffer size 1048576 javaagent D IntelliJ IDEA 2019 3 4 lib idea rt
  • excel巧用拼接函数CONCATENATE输出JSON字符串

    使用 CONCATENATE 其中一个文本函数 将两个或多个文本字符串联接为一个字符串 在 Excel 2016 Excel Mobile 和 Excel 网页版 中 此函数已替换为 CONCAT 函数 语法 CONCATENATE tex
  • gitkraken免费版本6.5.1,Linux下载地址

    来源 https www kaisawind com 2020 03 04 2020 03 05 git gitkraken自6 5 3本地库开始收费 所以推荐下载旧版本下载地址 收费版本 version 6 5 3 url deb htt
  • 视觉架构大一统!港中文通过统一视角Container对Transformer, 深度卷积以及MLP-Mixer进行了大一统

    编辑 Happy 首发 AIWalker 前段时间MLP Mixer提出后 引发了视觉架构圈的一篇轰动 包含但不限于以下几篇文章 重参数宇宙 再添新成员 RepMLP 清华大学 旷视科技提出将重参数卷积嵌入到全连接层 新坑 谷歌提出MLP
  • 在Final Cut Pro X 中如何把音频和视频对齐?

    在拍摄的视频的时候 往往因为现场的环境嘈杂而使得录音不清晰 我们通常会单独用一个设备录音 获得单条音频 在用Final Cut Pro X剪辑上的时候 如何把音频和视频对齐呢 用Final Cut Pro X把音频和视频对齐教程 1 在事件
  • muduo_base库学习笔记6——互斥量、条件变量和倒计时门闩类

    muduo中对互斥量和条件变量的操作基本上都是调用它们对应的相关函数来实现的 例如MutexLock lock即调用pthread mutex lock Condition wait 即调用pthread cond wait等 互斥量 mu
  • C - Balanced Number HDU - 3709 (数位dp)

    题目链接 https cn vjudge net contest 278036 problem C 题目大意 手首先是T组数据 然后每一次输入两个数l r 求这个区间里面满足以某个数字为中心的两侧力矩和相等的个数 举个例子 4139 我们如
  • win10键盘锁住了怎么解决

    有win10系统用户在使用的时候 发现键盘被锁住了 导致无法使用 经过分析可能是不小心按到了键盘上的锁住键 锁定键盘的快捷键 笔记本电脑 Fn Numlock 键 第一种方法 1 外接键盘 是否按过 Numlock 键 如果按过 Numlo
  • vue2使用国际化语言vue-i18n

    1 安装 npm install vue i18n 或 yarn add vue i18n 2 引用 在main js中引用 import VueI18n from vue i18n Vue use VueI18n 运行项目 报错 由于使用
  • 网络流之最大流和最小割

    最大流问题 最大流 给定有向图中每条边的最大流量 容量 求从源点到汇点的最大流量 容量网络 括号左边代表容量 右边代表流量 残留网络 流网络中剩余可增加的流量 增广路 满足容量条件的一条流量不为零的路径 增广路定理 设容量网络G V E 的
  • 转: Windows 批处理大全

    批处理文件是无格式的文本文件 它包含一条或多条命令 它的文件扩展名为 bat 或 cmd 在命令提示下键入批处理文件的名称 或者双击该批处理文件 系统就会调用Cmd exe按照该文件中各个命令出现的顺序来逐个运行它们 使用批 处理文件 也被
  • 【云原生之Docker实战】使用Docker部署NodeBB社区平台

    云原生之Docker实战 使用Docker部署NodeBB社区平台 一 NodeBB 二 检查本地docker环境 1 检查docker版本 2 检查docker状态 3 检查docker compose版本 三 下载NodeBB镜像 四
  • 方差(variance)、标准差(Standard Deviation)、均方差、均方根值(RMS)、均方误差(MSE)、均方根误差(RMSE)

    方差 variance 衡量随机变量或一组数据时离散程度的度量 概率论中方差用来度量随机变量和其数学期望 即均值 之间的偏离程度 统计中的方差 样本方差 是每个样本值与全体样本值的平均数之差的平方值的平均数 概率论中的方差表示方法 样本方差
  • Javascript设计模式-07-观察者模式(发布订阅模式)

    Javascript设计模式 07 观察者模式 发布订阅模式 简介 观察者模式又叫发布订阅模式 他定义了一种一对多的关系 让多个观察者对象同时监听某一个主体对象 这个主体对象发生改变时就会通知所有观察者 使得他们能够自动更新自己 提供一种抽
  • 多维时序

    多维时序 MATLAB实现Attention LSTM 注意力机制长短期记忆神经网络 多输入单输出 目录 多维时序 MATLAB实现Attention LSTM 注意力机制长短期记忆神经网络 多输入单输出 基本介绍 模型背景 LSTM模型
  • 【SQLMap工具-1】SQLMap简介及简单应用实例

    1 SQLMap简介 SQLMap 是一个自动化的SQL注入工具 其主要功能是扫描 发现并利用给定URL的SQL注入漏洞 内置了很多绕过插件 支持的数据库是MySQL Oracle PostgreSQL Microsoft SQL Serv
  • fmt文本格式库的源码下载编译(Win10+VS2022)

    目录 fmt文件格式库 什么是fmt fmt下载方式 资源结构 编译fmt 查看编译成功lib和pdb fmt文件格式库 什么是fmt fmt 是一个先进的文本格式库 具有现代语言的特征 用来代替 C 的 stdio 和 C iostrea
  • MessagePack For C#在Unity中的应用(场景的保存和加载)

    最近项目里面有这样一个需求 就是把场景里面的东西保存下载 然后需要的时候读取数据 加载出来 这里用到了MessagePack For C 的Unity 版本 先放一个网址https www cnblogs com Leo wl p 8143
  • 在数据集上计算连续随机变量的信息熵和互信息--k-近邻估计方法

    写在前面 信息熵 entropy 的原始定义是离散 discrete 的 后来发展了在连续域上的微分熵 differential entropy 然而 通常在给定的数据集上 无法知道连续变量的概率分布 其概率密度函数也就无法获得 不能够用微
  • Py_buffer

    使用C C 扩展Python之一 Posted on Thu 12 November 2015 in Python 假设我们需要使用C C 实现一个翻转字符串的扩展功能 下面是C语言的实现 include