实际C++的类是很难直接导出为Python的类,这缘于C++在编译的时候有一个过程就是修饰命名。但是在C中导出Python还是可以的,这个就是Python提供的C接口来实现(CPython)。这个主题就是实现一个C扩展模块提供一个Python类扩展。

C++扩展类实现

基本的头文件

#include

#include

#include

#include

iostream用户标准IO输入输出

这里只使用标准输出:std::cout

sstream是字符串处理/内存处理

字符串拷贝

Python的C API接口实现

大部分API都是这个头文件一共声明。

structmember定义了结构体的类型定义

T_STRING/T_INT等

PyMemberDef数据成员的定义

数据成员

数据成员定义

使用结构体定义数据,这一点不奇怪。实施Python的函数,类都是用C扩展实现。实际上没有C++什么事儿,就算我们人为使用了部分C++语法,但是从底层本质来说还是是C的实现最方便。

typedef struct _Sobel{

PyObject_HEAD

char *m_filename;

int m_fd;

} Sobel;

数据成员描述

数据成员描述,使用的是PyMemberDef类型数组,PyMemberDef这是一个结构体,其成员是数据成员需要描述的5个信息。

typedef struct PyMemberDef {

char *name; // 成员名

int type; // 类型

Py_ssize_t offset; // 偏离位置

int flags; // 属性修饰标识READONLY , READ_RESTRICTED, PY_WRITE_RESTRICTED,RESTRICTED

char *doc; // 成员的doc文档描述

} PyMemberDef;

描述上面定义的数据如下

最后空的一个记录,表示结束。

static PyMemberDef m_data[] = {

{"m_filename", T_STRING, offsetof(Sobel, m_filename), 0, "bmp file name"},

{"m_fd", T_INT, offsetof(Sobel, m_fd), 0, "file descriptor"},

{NULL, NULL, NULL, 0, NULL}

};

数据成员初始化

核心还是参数分析,与返回值处理,但是初始化没有返回值。

下面的逻辑是通过命名参数filename找到参数名,并初始化成员。

static void Sobel_init(Sobel *self, PyObject*args, PyObject*kwargs){

const char* filename;

static char *argsname[] = {"filename", NULL};

if(!PyArg_ParseTupleAndKeywords(args, kwargs, "s", argsname, &filename)){

std::cout << "parse init parameter error!" << std::endl;

return;

}

self->m_filename = new char[strlen(filename) + 1];

errno_t er = strcpy_s(self->m_filename, strlen(filename) + 1, filename);

std::cout << "init ok:" << self->m_filename << std::endl;

}

数据成员的释放

释放是主要是释放成员的内存空间

static void Sobel_destruct(Sobel *self){

if(self->m_filename){

delete[] self->m_filename;

}

}

函数成员

函数成员定义

函数与构造器与虚构器的差别是不管有返回值没有,都有PyObject指针返回。

这里实现了open与rotate(简单实现)

open模拟打开文件

rotate模拟旋转图像

static PyObject* Sobel_open(Sobel* self){

std::cout << "Open file ok" << std::endl;

return Py_BuildValue("s", self->m_filename);;

}

static PyObject *Sobel_rotate(Sobel *self, PyObject *args){

float angle;

if(!PyArg_ParseTuple(args, "f", &angle)){

return Py_None;

}

std::cout << "rotate OK:" << angle << std::endl;

return Py_None;

}

函数成员描述

描述函数使用PyMethodDef结构体,多个函数使用PyMethodDef数组来描述。

struct PyMethodDef {

const char *ml_name; /* Python中调用的函数名*/

PyCFunction ml_meth; /* C++实现的函数指针,只要类型转换,三种类型的函数(keywords参数函数,元组参数的函数,没有参数的函数) */

int ml_flags; /* 标记用来指定函数的参数的三种方式:METH_VARARGS, METH_KEYWORDS, METH_NOARGS */

const char *ml_doc; /* 函数的doc文档 */

};

typedef struct PyMethodDef PyMethodDef;

上面open与rotate函数的描述

static PyMethodDef m_functions[] = {

{"open", (PyCFunction)Sobel_open, METH_NOARGS, "open image file of bmp format"},

{"rotate", (PyCFunction)Sobel_rotate, METH_VARARGS, "rotate image"},

{NULL, NULL, NULL, NULL}

};

模块初始化的工作

模块的初始化只要是创建模块,并绑定一个Python的类型(元类对象:元类就是类型的类型,实例化后就是我们一般意义上的类)

模块描述

注意:这类的函数不能在模块中描述,在模块中描述的函数与数据,导出为Python的全局函数与数据。

模块描述采用PyModuleDef结构体,PyModuleDef结构体定义如下:

typedef struct PyModuleDef{

PyModuleDef_Base m_base; // 模块的开始地址。一般使用固定的宏PyModuleDef_HEAD_INIT

const char* m_name; // 模块名(安装的时候需要使用的名字)

const char* m_doc; // 模块的文档

Py_ssize_t m_size; // 模块的大小,一般使用-1表示自动确定

PyMethodDef *m_methods; // 全局函数

struct PyModuleDef_Slot* m_slots;

traverseproc m_traverse;

inquiry m_clear;

freefunc m_free;

} PyModuleDef;

#ifdef __cplusplus

}

我们创建的模块描述如下:

static PyModuleDef def_module = {

PyModuleDef_HEAD_INIT,

"sobel",

"This is doc for Class Sobel!",

-1,

NULL, // 我们的函数没有在这儿绑定

NULL,

NULL,

NULL,

NULL

};

类型对象描述(Python类)

Python的类是元类的对象,所以这儿描述的是元类对象。元类对象的描述也是使用C结构体, 这个结构体太彪悍,彪悍的人生不要解释,但是可以通过可阅读的成员名,可以知道其表示的含义。

typedef struct _typeobject {

PyObject_VAR_HEAD

const char *tp_name; /* For printing, in format "." */

Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */

/* Methods to implement standard operations */

destructor tp_dealloc;

printfunc tp_print;

getattrfunc tp_getattr;

setattrfunc tp_setattr;

PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)

or tp_reserved (Python 3) */

reprfunc tp_repr;

/* Method suites for standard classes */

PyNumberMethods *tp_as_number;

PySequenceMethods *tp_as_sequence;

PyMappingMethods *tp_as_mapping;

/* More standard operations (here for binary compatibility) */

hashfunc tp_hash;

ternaryfunc tp_call;

reprfunc tp_str;

getattrofunc tp_getattro;

setattrofunc tp_setattro;

/* Functions to access object as input/output buffer */

PyBufferProcs *tp_as_buffer;

/* Flags to define presence of optional/expanded features */

unsigned long tp_flags;

const char *tp_doc; /* Documentation string */

/* Assigned meaning in release 2.0 */

/* call function for all accessible objects */

traverseproc tp_traverse;

/* delete references to contained objects */

inquiry tp_clear;

/* Assigned meaning in release 2.1 */

/* rich comparisons */

richcmpfunc tp_richcompare;

/* weak reference enabler */

Py_ssize_t tp_weaklistoffset;

/* Iterators */

getiterfunc tp_iter;

iternextfunc tp_iternext;

/* Attribute descriptor and subclassing stuff */

struct PyMethodDef *tp_methods;

struct PyMemberDef *tp_members;

struct PyGetSetDef *tp_getset;

struct _typeobject *tp_base;

PyObject *tp_dict;

descrgetfunc tp_descr_get;

descrsetfunc tp_descr_set;

Py_ssize_t tp_dictoffset;

initproc tp_init;

allocfunc tp_alloc;

newfunc tp_new;

freefunc tp_free; /* Low-level free-memory routine */

inquiry tp_is_gc; /* For PyObject_IS_GC */

PyObject *tp_bases;

PyObject *tp_mro; /* method resolution order */

PyObject *tp_cache;

PyObject *tp_subclasses;

PyObject *tp_weaklist;

destructor tp_del;

/* Type attribute cache version tag. Added in version 2.6 */

unsigned int tp_version_tag;

destructor tp_finalize;

#ifdef COUNT_ALLOCS

/* these must be last and never explicitly initialized */

Py_ssize_t tp_allocs;

Py_ssize_t tp_frees;

Py_ssize_t tp_maxalloc;

struct _typeobject *tp_prev;

struct _typeobject *tp_next;

#endif

} PyTypeObject;

我们实现的Python类描述如下:

static PyTypeObject classinfo = {

PyVarObject_HEAD_INIT(NULL, 0)

"This is doc for Module",

sizeof(Sobel),

0,

(destructor)Sobel_destruct,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

"This is class for Sobel!",

0,

0,

0,

0,

0,

0,

m_functions,

m_data,

0,

0,

0,

0,

0,

0,

(initproc)Sobel_init,

0,

PyType_GenericNew,

0,

0,

0,

0,

0,

0,

0,

0

};

注意:其中PyVarObject_HEAD_INIT(NULL, 0)的含义也是确定头地址的。

#define PyObject_HEAD PyObject ob_base;

#define PyObject_HEAD_INIT(type) \

{ _PyObject_EXTRA_INIT \

1, type },

#define PyVarObject_HEAD_INIT(type, size) \

{ PyObject_HEAD_INIT(type) size },

模块初始化函数定义

这个函数按照命名预定的:PyInit_模块名

PyMODINIT_FUNC PyInit_sobel(void){

PyObject* mod;

// 1. 创建模块

// 2. 添加类型到导出的模块

return mod;

}

创建模块

创建模块使用PyModule_Create函数,函数原型如下

PyAPI_FUNC(PyObject *) PyModule_Create2(struct PyModuleDef*, int apiver);

#define PyModule_Create(module) PyModule_Create2(module, PYTHON_API_VERSION)

#define PYTHON_API_VERSION 1013

mod = PyModule_Create(&def_module);

if(mod == 0){

return Py_None;

}

Py_INCREF(&def_module);

创建成功,增加一次引用计数。

添加类型对象到模块(Python类)

添加模块使用函数PyModule_AddObject实现,函数定义如下:

PyAPI_FUNC(int) PyModule_AddObject(PyObject *, const char *, PyObject *);

在添加Python类之前,建议检测下是否描述完备,这个函数是PyType_Ready

PyAPI_FUNC(int) PyType_Ready(PyTypeObject *);

我们把我们描述的类直接加入即可:

if(PyType_Ready(&classinfo) < 0){

return Py_None;

}

PyModule_AddObject(mod, "Sobel", (PyObject*)&classinfo);

完整的模块导出的初始化工作实现

PyMODINIT_FUNC PyInit_sobel(void){

PyObject* mod;

if(PyType_Ready(&classinfo) < 0){

return Py_None;

}

mod = PyModule_Create(&def_module);

if(mod == 0){

return Py_None;

}

Py_INCREF(&def_module);

PyModule_AddObject(mod, "Sobel", (PyObject*)&classinfo);

return mod;

}

编译与测试

编译脚本setup.py

from distutils.core import setup, Extension

setup(

name="=sobel",

version="1.0",

ext_modules=[

Extension("sobel", sources=["sobel.cpp"], language="C++"),

]

)

编译命令

命令:

python setup.py build_ext --inplace

编译过程截图

编译过程

编译后生成动态库文件:

sobel.cp36-win_amd64.pyd

测试Python程序test.py

from sobel import *

# help(Sobel)

s = Sobel("gpu.bmp")

print("调用返回:",s.open())

s.rotate(45)

执行后结果如下:

在Python中执行的结果

这么繁琐的C扩展,我想很多人内心是顽皮马儿跑过,所以还有一个东西可以考虑学学Cython。实际Cython的性能是否真比C高,这个估计需要评估下,使用C是王道啊。

导出的文档帮助如下

from sobel import *

help(Sobel)

Help on class This is doc for Module in module builtins:

class This is doc for Module(object)

| This is class for Sobel!

|

| Methods defined here:

|

| __init__(self, /, *args, **kwargs)

| Initialize self. See help(type(self)) for accurate signature.

|

| __new__(*args, **kwargs) from builtins.type

| Create and return a new object. See help(type) for accurate signature.

|

| open(...)

| open image file of bmp format

|

| rotate(...)

| rotate image

|

| ----------------------------------------------------------------------

| Data descriptors defined here:

|

| m_fd

| file descriptor

|

| m_filename

| bmp file name

附录:

说明

本主题的内容还是没有实现C++的类被导出到Python,仅仅是利用C实现了一个Python类而已。

要导出C++类还需要二次调用。

官方的文档地址如下:

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

完整的代码:sobel.cpp

这个代码使用C也可以一样实现,使用C++是我们估计的,这个里面的额核心语法都是C。

#include

#include

#include

#include

typedef struct _Sobel{

PyObject_HEAD

char *m_filename;

int m_fd;

} Sobel;

static PyMemberDef m_data[] = {

{"m_filename", T_STRING, offsetof(Sobel, m_filename), 0, "bmp file name"},

{"m_fd", T_INT, offsetof(Sobel, m_fd), 0, "file descriptor"},

{NULL, NULL, NULL, 0, NULL}

};

static void Sobel_init(Sobel *self, PyObject*args, PyObject*kwargs){

const char* filename;

static char *argsname[] = {"filename", NULL};

if(!PyArg_ParseTupleAndKeywords(args, kwargs, "s", argsname, &filename)){

std::cout << "parse init parameter error!" << std::endl;

return;

}

self->m_filename = new char[strlen(filename) + 1];

errno_t er = strcpy_s(self->m_filename, strlen(filename) + 1, filename);

std::cout << "init ok:" << self->m_filename << std::endl;

}

static void Sobel_destruct(Sobel *self){

if(self->m_filename){

delete[] self->m_filename;

}

}

static PyObject* Sobel_open(Sobel* self){

std::cout << "Open file ok" << std::endl;

return Py_BuildValue("s", self->m_filename);;

}

static PyObject *Sobel_rotate(Sobel *self, PyObject *args){

float angle;

if(!PyArg_ParseTuple(args, "f", &angle)){

return Py_None;

}

std::cout << "rotate OK:" << angle << std::endl;

return Py_None;

}

static PyMethodDef m_functions[] = {

{"open", (PyCFunction)Sobel_open, METH_NOARGS, "open image file of bmp format"},

{"rotate", (PyCFunction)Sobel_rotate, METH_VARARGS, "rotate image"},

{NULL, NULL, NULL, NULL}

};

static PyTypeObject classinfo = {

PyVarObject_HEAD_INIT(NULL, 0)

"This is doc for Module",

sizeof(Sobel),

0,

(destructor)Sobel_destruct,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

0,

"This is class for Sobel!",

0,

0,

0,

0,

0,

0,

m_functions,

m_data,

0,

0,

0,

0,

0,

0,

(initproc)Sobel_init,

0,

PyType_GenericNew,

0,

0,

0,

0,

0,

0,

0,

0

};

static PyModuleDef def_module = {

PyModuleDef_HEAD_INIT,

"sobel",

"This is doc for Class Sobel!",

-1,

NULL,

NULL,

NULL,

NULL,

NULL

};

PyMODINIT_FUNC PyInit_sobel(void){

PyObject* mod;

if(PyType_Ready(&classinfo) < 0){

return Py_None;

}

mod = PyModule_Create(&def_module);

if(mod == 0){

return Py_None;

}

Py_INCREF(&def_module);

PyModule_AddObject(mod, "Sobel", (PyObject*)&classinfo);

return mod;

}

python类的mod_PY08-06:Python的类扩展相关推荐

  1. python精通-11周精通python计划(完结)-网易云课堂

    微专业 11周精通python计划(完结) 课程概况 Python 语言是计算机工程.大数据及人工智能等领域的基础性语言,广泛且深刻地影响着信息技术各领域的发展方式及速度,从软件开发到硬件开发.从数据 ...

  2. python培训班时间 费用-深圳python培训班大概多少费用

    深圳python培训班大概多少费用 来源:教育联展网 编辑:粉色de皮卡丘 发布时间:2019-08-20 人工智能Python培训 到北大青鸟学Python 技能提升高薪就业 快速咨询 学习Pyth ...

  3. 【Python】Python语言学习:面向对象编程,类和对象,封装、继承和多态

    这一周Python语言学习,记录如下. 01 面向对象编OOP 1.1 为什么学习和应用OOP? 1 OOP适合更加复杂的需求分析和项目开发. 2 OOP具有更强大的封装能力. 3 OOP相比于面向过 ...

  4. Python实现VIIRS气溶胶产品重投影-类GLT实现

    Python实现VIIRS气溶胶产品重投影-类GLT实现 前言 代码说明 用到的外部python包 代码实现 注意事项 后记 前言 最近用IDL写了个VIIRS气溶胶产品重投影的代码,之后心想着用py ...

  5. python如何创建一个类_python (知识点:类)简单的创建一个类

    #!/usr/bin/env python # -*- coding: utf-8 -*- """ Created on Mon Nov 14 01:01:29 2016 ...

  6. 《易学Python》——第6章 类与面向对象编程 6.1 类是什么

    本节书摘来自异步社区<易学Python>一书中的第6章,第6.1节,作者[澳]Anthony Briggs,王威,袁国忠 译,更多章节内容可以访问云栖社区"异步社区"公 ...

  7. python中的继承有什么特点_python类的继承是什么?类的继承有什么样的规则?

    在这篇文章之中我们来了解一下python类的继承,对于刚刚接触到python这一编程语言的朋友来说,对于python类的继承的了解应该比较少,不过没关系,在接下来的文章之中我们就来了解一下python ...

  8. python中类方法与实例方法的区别-Python中的对象,方法,类,实例,函数用法分析...

    本文实例分析了Python中的对象,方法,类,实例,函数用法.分享给大家供大家参考.具体分析如下: Python是一个完全面向对象的语言.不仅实例是对象,类,函数,方法也都是对象. class Foo ...

  9. python装饰器实例-基于Python 装饰器装饰类中的方法实例

    title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] categ ...

  10. python中的类及self详解_Python类class参数self原理解析

    1.self只有在类的方法中才会有,其他函数或方法是不必带self的. 2.在调用时不必传入相应的参数. 3.在类的方法中(如__init__),第一参数永远是self,表示创建的类实例本身,而不是类 ...

最新文章

  1. win10访问不了局域网计算机名,Win10正式版无法访问局域网电脑怎么办
  2. samba+quota配置
  3. windows 基础及基本软件测试环境搭建
  4. UA MATH566 用Basu定理证明统计量不完备
  5. noi99钉子和小球 解题报告
  6. Go 语言新提案:添加模糊测试支持
  7. 【2016.11.16】HTML学习笔记
  8. 阿里云飞天洛神2.0:开放弹性的云网络NFV平台
  9. dexpress 流程图_DevExpress常用操作
  10. 【数字逻辑设计】Logisim构建锁存器/触发器
  11. Ubuntu 18.04 与 20.04 LTS 性能测试
  12. Eclipse中使用GIT将文件还原至上一版本
  13. Mac多功能文件搜索软件:HoudahSpot
  14. 奇怪的 Win10 输入法问题
  15. 数字科技陪伴企业成长|突破封锁,庚顿数据助力中国名牌全球瞩目
  16. R语言中同比增长和环比增长
  17. 养生之道?阴阳平衡也!
  18. 【编程笔试】美团2021校招笔试-通用编程题第9场(附思路及C++代码)
  19. 第九届大唐杯国赛获奖名单
  20. Python之建模规划篇--线性规划

热门文章

  1. mysql 字段最右匹配_Mysql字符串处理函数详细介绍、总结 -电脑资料
  2. QPS和并发数,究竟是何种关系?
  3. centos下载安装wget包
  4. 知识图谱构建的一般流程
  5. Linux中使用rpm命令卸载软件
  6. 解决 ubuntu图形界面无法打开,全屏都是【ok】,linux磁盘空间不足,pycharm添加桌面快捷方式,pip无法安装python库,火狐无法联网
  7. JBOSS EAP实战(1)
  8. osd max backfills
  9. [Windows编程] 使用AttachThreadInput 来捕捉其它窗口的键盘输入
  10. 计算机其他快捷方式如何删除,我的电脑其他组件图标怎么删除