如何使用纯 Python 扩展 API (python3) 包装 C++ 对象?

Posted

技术标签:

【中文标题】如何使用纯 Python 扩展 API (python3) 包装 C++ 对象?【英文标题】:How to wrap a C++ object using pure Python Extension API (python3)? 【发布时间】:2018-02-14 11:58:32 【问题描述】:

我想知道如何在没有外部工具(如 Cython、Boost、SWIG 等)的情况下使用 Python Extension API(和 distutils)包装 C++ 对象。只是以纯 Python 方式,无需创建 dll。

请注意,我的 C++ 对象具有内存分配,因此必须调用析构函数以避免内存泄漏。

#include "Voice.h"

namespace transformation
 
  Voice::Voice(int fftSize)  mem=new double[fftSize];  

  Voice::~Voice()  delete [] mem;  

  int Voice::method1()  /*do stuff*/ return (1);  

我只想在 Python 中做类似的事情:

import voice

v=voice.Voice(512)
result=v.method1()

【问题讨论】:

除非您阅读并理解docs.python.org/3.6/c-api/index.html,否则您将无法做到这一点 好的。但也许你有一个例子的链接。示例通常比官方文档更容易理解(即使它在您想深入了解时很有用)。我什么都没找到。 以python模块源码为例:github.com/python/cpython/tree/master/Modules Boost.Python、SWIG 等提供的价值是你不必知道/理解所有底层细节。 我不敢相信 Python 设计者没有提供一个简单的方法来做到这一点。 【参考方案1】:

似乎答案实际上就在这里:https://docs.python.org/3.6/extending/newtypes.html

有例子,但并不容易。

编辑 1:

实际上,它并不是真正将 C++ 对象包装在 Python 对象中,而是用 C 代码创建 Python 对象。 (edit2:所以你可以包装 C++ 对象!)

编辑 2:

这是使用Python newtypes的解决方案

.

原始 C++ 文件:Voice.cpp

#include <cstdio>

#include "Voice.h"

namespace transformation
 
    Voice::Voice(int fftSize) 
        printf("c++ constructor of voice\n");
        this->fftSize=fftSize;
        mem=new double[fftSize];
         

    Voice::~Voice()  delete [] mem;  

    int Voice::filter(int freq) 
        printf("c++ voice filter method\n");
        return (doubleIt(3));
    
    int Voice::doubleIt(int i)  return 2*i; 

.

原始h文件:Voice.h

namespace transformation 

    class Voice 
    public:
        double *mem;
        int fftSize;

        Voice(int fftSize);
        ~Voice();

        int filter(int freq);
        int doubleIt(int i);
    ;


.

C++ Python 封装文件:voiceWrapper.cpp

#include <Python.h>

#include <cstdio>
//~ #include "structmember.h"

#include "Voice.h"

using transformation::Voice;

typedef struct 
    PyObject_HEAD
    Voice * ptrObj;
 PyVoice;




static PyModuleDef voicemodule = 
    PyModuleDef_HEAD_INIT,
    "voice",
    "Example module that wrapped a C++ object",
    -1,
    NULL, NULL, NULL, NULL, NULL
;

static int PyVoice_init(PyVoice *self, PyObject *args, PyObject *kwds)
// initialize PyVoice Object

    int fftSize;

    if (! PyArg_ParseTuple(args, "i", &fftSize))
        return -1;

    self->ptrObj=new Voice(fftSize);

    return 0;


static void PyVoice_dealloc(PyVoice * self)
// destruct the object

    delete self->ptrObj;
    Py_TYPE(self)->tp_free(self);



static PyObject * PyVoice_filter(PyVoice* self, PyObject* args)

    int freq;
    int retval;

    if (! PyArg_ParseTuple(args, "i", &freq))
        return Py_False;

    retval = (self->ptrObj)->filter(freq);

    return Py_BuildValue("i",retval);



static PyMethodDef PyVoice_methods[] = 
     "filter", (PyCFunction)PyVoice_filter,    METH_VARARGS,       "filter the mem voice" ,
    NULL  /* Sentinel */
;

static PyTypeObject PyVoiceType =  PyVarObject_HEAD_INIT(NULL, 0)
                                    "voice.Voice"   /* tp_name */
                                ;


PyMODINIT_FUNC PyInit_voice(void)
// create the module

    PyObject* m;

    PyVoiceType.tp_new = PyType_GenericNew;
    PyVoiceType.tp_basicsize=sizeof(PyVoice);
    PyVoiceType.tp_dealloc=(destructor) PyVoice_dealloc;
    PyVoiceType.tp_flags=Py_TPFLAGS_DEFAULT;
    PyVoiceType.tp_doc="Voice objects";
    PyVoiceType.tp_methods=PyVoice_methods;
    //~ PyVoiceType.tp_members=Noddy_members;
    PyVoiceType.tp_init=(initproc)PyVoice_init;

    if (PyType_Ready(&PyVoiceType) < 0)
        return NULL;

    m = PyModule_Create(&voicemodule);
    if (m == NULL)
        return NULL;

    Py_INCREF(&PyVoiceType);
    PyModule_AddObject(m, "Voice", (PyObject *)&PyVoiceType); // Add Voice object to the module
    return m;

.

distutils 文件:setup.py

from distutils.core import setup, Extension

setup(name='voicePkg', version='1.0',  \
      ext_modules=[Extension('voice', ['voiceWrapper.cpp','Voice.cpp'])])

.

python 测试文件:test.py

import voice

v=voice.Voice(512)
result=v.filter(5)
print('result='+str(result))

.

还有魔法:

sudo python3 setup.py install
python3 test.py

输出是:

c++ constructor of voice
c++ voice filter method
result=6

享受吧!

厄运

【讨论】:

以上是关于如何使用纯 Python 扩展 API (python3) 包装 C++ 对象?的主要内容,如果未能解决你的问题,请参考以下文章

纯Python实现Torch API,康奈尔副教授为自己的课程创建了DIY教学库

纯Python实现Torch API,康奈尔副教授为自己的课程创建了DIY教学库

aliyun域名解析python api

微软开源 Python 自动化神器 Playwright

如何使用 PyList 在 Python C API 扩展中返回整数列表?

使用Python扩展lldb