【问题标题】:How to store python objects in Cython C++ containers?如何在 Cython C++ 容器中存储 python 对象?
【发布时间】:2014-11-14 19:12:21
【问题描述】:

我想将带有 的现有 库移植到Python,C++ 库使用。在这种情况下,它是adevs library

问题是如何使用 Cython 将 Python 对象存储在 C++ 容器中?我知道这在某种程度上是discouraged,用于引用计数的问题,但是可以这样做吗?如果可以,怎么做?

我知道Gauthier Boaglios' answersimilar question。但是,这显然不能解决引用计数的问题,因为我尝试了以下操作:

假设在 'cadevs.pxd' 我有以下代码:

cdef extern from "adevs/adevs_digraph.h" namespace "adevs":
cdef cppclass PortValue[VALUE, PORT]:
    PortValue() except +
    PortValue(PORT port, const VALUE& value) except +
    PORT port
    VALUE value

在'adevs.pyx'中:

from cpython.ref cimport PyObject
cimport cadevs

ctypedef PyObject* PythonObject

cdef class PortValue:
    cdef cadevs.PortValue[PythonObject, PythonObject]* _c_portvalue

    def __cinit__(self, object port, object value):
        self._c_portvalue = new cadevs.PortValue[PythonObject, PythonObject](
            <PyObject *>port, <PyObject *>value
        )

    def __dealloc__(self):
        del self._c_portvalue

    property port:
        def __get__(self):
            return <object>self._c_portvalue.port

    property value:
        def __get__(self):
            return <object>self._c_portvalue.value

然后我 cythonize 并编译

$ cython --cplus -3 adevs.pyx
$ g++ -shared -pthread -fPIC -fwrapv -O2 -Wall -I/usr/include/python3.4m -I../include -lpython3.4m -o adevs.so adevs.cpp

但是在 python 或 ipython 中运行

import adevs
pv = adevs.PortValue((1,2), 3)
pv.port
pv.port

显然,当对 (1, 2) 元组的引用丢失时,Python 崩溃了。

【问题讨论】:

标签: c++ cython templates python c++ templates cython


【解决方案1】:

您是对的,通过将 python 对象存储在带有 Cython 的 C++ 容器中,您将难以运行内存安全的应用程序。如果您想在 Cython 而不是 Pybind11 中执行此操作(如 Mike MacNeil 的回答所引用),那么您有很多选择。

  1. 将值存储在 Cython/Python 中的某处,以在对象位于容器中时将引用计数保持在 1 以上。示例:
cdef class PortValue:
    cdef cadevs.PortValue[PythonObject, PythonObject]* _c_portvalue

    # Add fields to keep stored Python objects alive.
    cdef object port_ref_holder 
    cdef object value_ref_holder 


    def __cinit__(self, object port, object value):
        self._c_portvalue = new cadevs.PortValue[PythonObject, PythonObject](
            <PyObject *>port, <PyObject *>value
        )

        # Assign objects here to keep them alive.
        port_ref_holder = port
        value_ref_holder = value
  1. 您可以使用 Python C-API 和 Wrapper 来手动增加和减少引用。用于引用计数的 Python C API 参考是 here。 cython 包在 Cython 中自动为您提供此 API,作为您可以 cimport 的 cython 声明 (.pxd) 文件(请参阅here)。我可以在单独的 C++ 文件中添加引用计数功能,或者我可以根据Interfacing with C guide 将这段代码直接逐字添加到 Cython。这样的事情是一个开始:
from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF
cdef extern from *:
    """
    class PyRef {
        PyObject* obj;
    public:
        
        PyObject* get() {return obj;}
        PyRef() {obj = NULL;}
        PyRef(PyObject* set_obj) {
            Py_XINCREF(set_obj); 
            obj = set_obj;}
        
        ~PyRef() {
            Py_XDECREF(obj);obj = NULL;
        }

        PyRef(const PyRef& other)  {
            Py_XINCREF(other.obj); 
            obj = other.obj;
        }
        PyRef(PyRef&& other) {obj = other.obj; other.obj = NULL;}

        PyRef& operator=(const PyRef& other) {
            Py_XDECREF(obj); 
            Py_XINCREF(other.obj); 
            obj = other.obj;
            return *this;
        }
        PyRef& operator=(PyRef&& other) {
            Py_XDECREF(obj); 
            obj = other.obj; 
            other.obj = NULL;
            return *this;
        }
    };
    """
    cdef cppclass PyRef:
        PyRef() except +
        PyRef(PyObject* set_obj) except +
        PyObject* get() except +
    

然后你使用“PyRef”类而不是 PythonObject 并使用它的 get() 方法来借用对存储的 python 对象的引用。

【讨论】:

    【解决方案2】:

    我不确定绑定到 Cython 可能会很困难,我建议使用 Pybind11,它可以(相对)简单地为 C++ 编写 python 包装器(有关面向对象的代码,请参阅 this particular example)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多