如何从double数组初始化元组,避免“存储临时对象的不安全C派生”错误?
问题
尝试从C语言的double数组初始化Python元组,编写了如下Cython代码:
from cpython.ref cimport PyObject from cpython.tuple cimport PyTuple_New from cpython.float cimport PyFloat_FromDouble cdef extern from "Python.h": struct PyTupleObject: PyObject *ob_item[1] # ====================================================================== # Tuple utility API # ====================================================================== cdef tuple from_doubles(unsigned n, const double* buffer): """ Create a tuple from an array of floats. """ cdef tuple t = PyTuple_New(n) cdef PyObject** items = (<PyTupleObject*>t).ob_item cdef unsigned i for i in range(n): items[i] = <PyObject*>PyFloat_FromDouble(buffer[i]) return t
认为PyFloat_FromDouble创建的新对象会被元组“窃取”引用,无需调用Py_INCREF/Py_DECREF,但使用Cython 0.26.1编译时,出现以下错误:
Error compiling Cython file: ------------------------------------------------------------ ... """ cdef tuple t = PyTuple_New(n) cdef PyObject** items = (<PyTupleObject*>t).ob_item cdef unsigned i for i in range(n): items[i] = <PyObject*>PyFloat_FromDouble(buffer[i]) ^ ------------------------------------------------------------ fin/tuplex.pyx:19:19: Casting temporary Python object to non-numeric non-Python type Error compiling Cython file: ------------------------------------------------------------ ... """ cdef tuple t = PyTuple_New(n) cdef PyObject** items = (<PyTupleObject*>t).ob_item cdef unsigned i for i in range(n): items[i] = <PyObject*>PyFloat_FromDouble(buffer[i]) ^ ------------------------------------------------------------ fin/tuplex.pyx:19:19: Storing unsafe C derivative of temporary Python reference
需要修正代码,将新创建的PyFloat对象正确存储到元组中。
解决方案
Cython的类型检查机制不允许直接将临时Python对象强制转换为PyObject*后存入C指针数组,因为它无法识别这种“引用窃取”的行为。正确的做法是使用Python C API提供的PyTuple_SET_ITEM宏,它的行为正是期望的——直接将对象指针存入元组,不增加引用计数,相当于“窃取”引用。
修正步骤
- 在
cdef extern from "Python.h"中声明PyTuple_SET_ITEM宏: - 替换直接操作
ob_item数组的代码,改为调用PyTuple_SET_ITEM。
修正后的完整代码
from cpython.ref cimport PyObject from cpython.tuple cimport PyTuple_New from cpython.float cimport PyFloat_FromDouble cdef extern from "Python.h": void PyTuple_SET_ITEM(PyObject* t, Py_ssize_t pos, PyObject* o) # ====================================================================== # Tuple utility API # ====================================================================== cdef tuple from_doubles(unsigned n, const double* buffer): """ Create a tuple from an array of floats. """ cdef tuple t = PyTuple_New(n) cdef unsigned i for i in range(n): PyTuple_SET_ITEM(<PyObject*>t, i, <PyObject*>PyFloat_FromDouble(buffer[i])) return t
关键说明
PyTuple_SET_ITEM是专门为元组初始化设计的宏,直接将对象指针写入元组内部数组,不会触发额外的引用计数操作,完全符合“引用窃取”的需求。- 使用该宏后,Cython能够正确识别代码语义,避免类型检查错误,同时保证代码符合Python C API的规范。
内容的提问来源于stack exchange,提问作者Sylvain Leroux
相关产品推荐
相关产品推荐

