You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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宏,它的行为正是期望的——直接将对象指针存入元组,不增加引用计数,相当于“窃取”引用。

修正步骤

  1. 在cdef extern from "Python.h"中声明PyTuple_SET_ITEM宏:
  2. 替换直接操作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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 12:28:17