如何从双精度数组初始化元组而不导致“存储临时的不安全的 C 派生”错误?

问题描述 投票:0回答:1

假设我知道自己在做什么,我尝试从 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

cython
1个回答
0
投票

我必须为

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

中所做的那样
  1. 我避免 Cython 生成错误的
    Py_DECREF
    调用,并且
  2. 我摆脱了“存储临时Python引用的不安全C派生”错误。

剩余代码仅供参考:

# ======================================================================
# 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)
© www.soinside.com 2019 - 2024. All rights reserved.