假设我知道自己在做什么,我尝试从 C 双精度数组初始化一个元组:
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
创建了一个新对象,当它存储在items[i]
中时,它被元组窃取。这就是
PyTuple_SET_ITEM
所做的,但没有多余的类型检查。据我了解,这里不需要 Py_INCREF
/Py_DECREF
调用。
来自医生:
很少有函数会窃取引用;两个值得注意的例外是 PyList_SetItem() 和 PyTuple_SetItem(),窃取对 item(但不是放入该项目的元组或列表!)。这些 由于常见的习惯用法,函数被设计为窃取引用 用于使用新创建的对象填充元组或列表;为了 例如,创建元组 (1, 2, “三”) 的代码可能如下所示 这(暂时忘记错误处理;更好的方法 代码如下所示):
PyObject *t; t = PyTuple_New(3); PyTuple_SetItem(t, 0, PyLong_FromLong(1L)); PyTuple_SetItem(t, 1, PyLong_FromLong(2L)); PyTuple_SetItem(t, 2, PyUnicode_FromString("three"));
但是,当我使用 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
?
我必须为
PyFloat_FromDouble
提供我自己的外部定义,以避免来自 Cython 的各种自动引用管理:
from cpython.ref cimport PyObject
from cpython.tuple cimport PyTuple_New
cdef extern from "Python.h":
PyObject* PyFloat_FromDouble(double v)
# Above:
# Do NOT use the definition from `cpython.float` to avoid
# automatic DECREF calls.
ctypedef struct PyTupleObject:
PyObject *ob_item[1]
通过将
PyFloat_FromDouble
定义为返回 PyObject*
而不是 object
,如 cpython.float
中所做的那样
Py_DECREF
调用,并且剩余代码仅供参考:
# ======================================================================
# 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] = PyFloat_FromDouble(buffer[i])
return t
def test():
cdef double[1000] values = list(range(1000))
return from_doubles(1000, values)