python - 使用 C/API 和 C++ 类编写 Python 模块

标签 python c++ c module

我是编写自定义 Python 模块的新手,我对 Capsules 的工作原理有点困惑。我使用系统 OSX 安装中的 Python 2.7.6,并尝试使用 Capsules(推荐用于 Python > 2.7)来传递指针(在他们为此使用 PyCObject 之前)。我的代码目前不起作用,我想了解一些原则上应该如何处理这里的事情。该代码应定义一个 LuscherClm 类,我希望能够执行以下操作:

>>> c40=Luscher(4,0)
>>>
>>> c40(0.12)
>>> <print the result of the evaluation>

第一个问题:目前我必须做类似的事情:

>>> c40=Luscher.init(4,0)
>>>
>>> c40.eval(0.12)
Segfault 

因此,我的第一个问题是:如何修改方法表以具有更多运算符样式的转换而不是成员函数 init 和 eval。

但是,我的代码还有其他问题,这里是相关部分(底层 C++ 类运行顺利,我在生产中经常使用它):

析构函数:

//destructor
static void clm_destruct(PyObject* capsule){
    void* ptr=PyCapsule_GetPointer(capsule,"zetfunc");
    Zetafunc* zetptr=static_cast<Zetafunc*>(ptr);
    delete zetptr;
    return;
}

构造函数:它返回指向胶囊的指针。我不知道这是否正确。因为在这种情况下,当我调用 clm=LuscherClm.init(l,m) 时,clm 对象是 PyCapsule 并且没有属性 eval,因此我无法对其调用 clm.eval(x)。应该如何处理?

//constructor
static PyObject* clm_init(PyObject* self, PyObject *args){
    //return value
    PyObject* result=NULL;

    //parse variables
    unsigned int lval=0;
    int mval=0;
    if(!PyArg_ParseTuple(args,"li",&lval,&mval)){
        ::std::cout << "Please specify l and m!" << ::std::endl;
        return result;
    }

    //class instance:
    Zetafunc* zetfunc=new Zetafunc(lval,mval);
    instanceCapsule=PyCapsule_New(static_cast<void*>   (zetfunc),"zetfunc",&clm_destruct);
    return instanceCapsule;
}

那么胶囊是如何传递给评估函数的呢?下面的代码不正确,因为我在从 CObjects 移动到 Capsules 后没有更新它。胶囊应该是一个全局变量(我不喜欢那样)还是如何将它传递给评估函数?或者我应该把它称为 self ,但此刻的 self 是什么?

//evaluate the function
static PyObject* clm_evaluate(PyObject* self, PyObject* args){
    //get the PyCObject from the capsule:
    void* tmpzetfunc=PyCapsule_GetPointer(instanceCapsule,"zetfunc");
    if (PyErr_Occurred()){
        std::cerr << "Some Error occured!" << std::endl;
        return NULL;
    }
    Zetafunc* zetfunc=static_cast< Zetafunc* >(tmpzetfunc);
    //parse value:
    double x;
    if(!PyArg_ParseTuple(args,"d",&x)){
        std::cerr << "Specify a number at which you want to evaluate the function" << std::endl;
        return NULL;
    }
    double result=(*zetfunc)(x).re();

    //return the result as a packed function:
    return Py_BuildValue("d",result);
}

//methods
static PyMethodDef LuscherClmMethods[] = {
    {"init",  clm_init, METH_VARARGS, "Initialize clm class!"},
    {"eval", clm_evaluate, METH_VARARGS, "Evaluate the Zeta-Function!"},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

Python < 3 初始化函数:

PyMODINIT_FUNC
initLuscherClm(void)
{
    PyObject *m = Py_InitModule("LuscherClm", LuscherClmMethods);
    return;
}

你能给我解释一下哪里出了问题以及为什么吗?如果可能的话,我想远离 SWIG 或 boost,因为这个模块应该很容易携带,而且我想避免每次我想在其他地方使用它时都必须安装额外的包。 此外:C/API 在调用函数时产生的开销是多少?我需要将它称为 O(10^6) 次的顺序,但我仍然希望它很快。

好的,我现在正在使用 boost.python,但是当我运行 object.eval() 时出现段错误。这是我现在的程序:

BOOST_PYTHON_MODULE(threevecd)
{
    class_< threevec<double> >("threevecd",init<double,double,double>());
}

BOOST_PYTHON_MODULE(LuscherClm)
{
    class_<Zetafunc>("LuscherClm",init<int,int, optional<double,threevec<double>,double,int> >())
    .def("eval",&Zetafunc::operator(),return_value_policy<return_by_value>());
    boost::python::to_python_converter<dcomplex,dcomplex_to_python_object>();
}

dcomplex 是我自己的复数实现。所以我不得不写一个转换器:

struct dcomplex_to_python_object
{
    static PyObject* convert(dcomplex const& comp)
    {
        if(fabs(comp.im())<std::numeric_limits<double>::epsilon()){
            boost::python::object result=boost::python::object(complex<double>(comp.re(),comp.im()));
            return boost::python::incref(result.ptr());
        }
        else{
            return Py_BuildValue("d",comp.re());
        }
    }
};

Complex128 是一个 numpy 扩展,boost 无法理解。所以我的问题是: 1) 我如何返回一个复数作为 python 数据类型(复数是标准的 python 类型吗?) 2)为什么我会出现段错误。我在测试用例中的结果是真实的,所以它应该默认为 else 语句。我猜指针超出了范围,仅此而已。但即使在 if 情况下(我关心引用增量),它也会出现段错误。有人可以帮我解决类型转换问题吗?

谢谢 托斯滕

最佳答案

好的,我知道了。以下转换器完成这项工作:

struct dcomplex_to_python_object
{
    static PyObject* convert(dcomplex const& comp)
    {
        PyObject* result;
        if(std::abs(comp.im())<=std::numeric_limits<double>::epsilon()){
            result=PyFloat_FromDouble(comp.re());
        }
        else{
            result=PyComplex_FromDoubles(comp.re(),comp.im());
        }
        Py_INCREF(result);
        return result;
    }
};

使用此转换器和 Wouter 的帖子,我想我的问题得到了解答。谢谢

关于python - 使用 C/API 和 C++ 类编写 Python 模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29948345/

相关文章:

python - 标准化包含过大值的数据集

c++ - 析构函数的奇怪行为

c++ - 创建带参数的 C++ 构造函数

c - 有没有很好的 Valgrind 替代 Windows?

c - 判断直线是平行的、重合的还是相交的。如果它们相交,找到交点

python - 让 Sphinx 检测对辅助配置/模板文件的更改

python - 在 CentOS 6.5 上安装 Matplotlib

python - 在 python 中 - 在嵌套字典中查找最大日期

c++ - 优化 : . cpp 或 .obj/.o 或 .lib/.a

c# - 对于高可伸缩性和性能需求,在 PHP 应用程序的后端使用 C++ 还是 Java 实现更可取?