【问题标题】:Passing Cython class object as argument to C function将 Cython 类对象作为参数传递给 C 函数
【发布时间】:2015-11-22 08:07:22
【问题描述】:

我正在尝试将 Cython 实例类对象作为参数传递给 C 函数 - 这样它就可以对其方法进行回调。

这是我尝试过的:

样本.c
#include "python.h"

void c_func(PyObject *obj){
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();

    /* Dictionary Object - can be of any dynamic type */

    PyObject *dict_obj = PyDict_New();
    PyObject *key = PyUnicode_FromString("average-run");
    PyObject *val = PyLong_FromLong(45445);
    PyDict_SetItem(dict_obj, key, val);

    /* Make a callback */

    PyObject_CallMethod(obj, "func_a", "(d)", dict_obj);

    PyGILState_Release(gstate);
}
pysample.pxd
cdef extern from "sample.h":
    void c_func(object obj)

cdef class Test(object):
    cdef func_a(self, object dict_obj)
pysample.pyx
cimport pysample
from pysample cimport Test

cdef class Test(object):
    cdef func_a(self, object dict_obj):
        print(dict_obj)

    def run_test(self):
        # Here I make a call to the C function
        pysample.c_func(self)

不幸的是,来自C 的回调不起作用。你能发现我做错了什么或建议解决这个问题吗?

【问题讨论】:

  • c_func 函数应该如何作为测试方法附加到这里? self.c_func 似乎应该给出属性错误。编译、导入、尝试调用时能显示错误吗?
  • 我错过了 pysample 的独立 cimport - 我将使用它编辑线程。谢谢
  • 我们仍然需要实际看到您编译、加载和调用 run_test,以便我们可以尝试帮助调试。
  • 文档中的两件事可能会有所帮助。 1) 格式字符串中的“d”被解释为浮点数,而不是字典 (docs.python.org/2/c-api/arg.html#c.Py_BuildValue)。 2)“请注意,如果你只通过PyObject * argsPyObject_CallMethodObjArgs()是一个更快的选择。” (docs.python.org/2/c-api/object.html)
  • 我强烈怀疑如果您将 callmethod 行更改为:PyObject_CallMethod(obj, "func_a", "(O)", dict_obj);(将 dict 作为通用 Python 对象传递),它将起作用。

标签: python c cython cpython


【解决方案1】:

问题已解决。这是因为类中的方法被定义为cdef,而不是cpdef

样本.c
#include "python.h"

void c_func(PyObject *obj){
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();

    /* Dictionary Object - can be of any dynamic type */

    PyObject *dict_obj = PyDict_New();
    PyObject *key = PyUnicode_FromString("average-run");
    PyObject *val = PyLong_FromLong(45445);
    PyDict_SetItem(dict_obj, key, val);

    /* Make a callback */

   PyObject *res = PyObject_CallMethod(obj, "func_a", "(O)", dict_obj);

   if( !res )
   {
       PyErr_Print();
   }else
   {
      Py_DECREF(res); 
   }
   /* 
    * Note: Do not remove reference counting
    * for callback argument - python will take
    * care of it when the callback python method
    * go out of scope. If you do - it will cause
    * inconsistent data behaviour at the callback 
    * method side.
    */

   PyGILState_Release(gstate);
}
pysample.pxd
cdef extern from "sample.h":
    void c_func(object obj)

cdef class Test(object):
    cpdef func_a(self, object dict_obj)
pysample.pyx
cimport pysample
from pysample cimport Test

cdef class Test(object):
    cpdef func_a(self, object dict_obj):
        print(dict_obj)

    def run_test(self):
        # Here I make a call to the C function
        pysample.c_func(self)

【讨论】:

  • 很高兴你把它整理好了!我第一次看时错过了cdef/cpdef/def 问题。您应该解决的另外两件事(但不是您的核心问题):1)c_func 不返回PyObject*(但您说它确实如此)。 2)PyObject_CallMethod 返回一个PyObject*(对于您的特定测试用例,可能为None),您应该减少它 - 如果您从func_a 返回任何非平凡的东西,您将有内存泄漏。跨度>
  • 另外(在c_func 的末尾):Py_DECREF(dict_obj); Py_DECREF(key); Py_DECREF(val); 并将调用更改为:PyObject* res = PyObject_CallMethod( /* as before */); if (!res) { PyErr_Print(); } else { Py_DECREF(res); }。 C-api 中涉及很多引用计数,这真的是一场噩梦。
  • 让我困惑的另一件事是有 2 个cdef class Test(object),导入的那个会被.pyx 文件覆盖吗?
  • 是的。将.pxd 视为接口,将.pyx 视为实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-21
  • 1970-01-01
  • 2012-02-27
  • 1970-01-01
  • 1970-01-01
  • 2021-04-15
  • 1970-01-01
相关资源
最近更新 更多