Punteros y “Almacenamiento de C inseguro derivado de referencia temporal de Python”

Estaba escribiendo código para almacenar un valor entero (potencialmente) muy grande en una matriz de chars referenciados por un puntero. Mi código se ve así:

 cdef class Variable: cdef unsigned int Length cdef char * Array def __cinit__(self, var, length): self.Length = length self.Array = malloc(self.Length * sizeof(char)) # Error for i in range(self.Length): self.Array[i] = (var >> (8 * i)) def __dealloc__(self): self.Array = NULL 

Cuando intenté comstackr el código, obtuve el error “Almacenar C derivado no seguro de referencia temporal de Python” en la línea comentada. Mi pregunta es la siguiente: ¿a qué referencia temporal de Python estoy derivando en C y almacenando, y cómo la arreglo?

El problema es que bajo el capó se crea una variable temporal para mantener la matriz antes de la asignación a self.Array y no será válida una vez que el método salga.

Tenga en cuenta que la documentación aconseja:

las funciones C-API para asignar memoria en el montón de Python generalmente se prefieren a las funciones C de bajo nivel anteriores, ya que la memoria que proporcionan se cuenta en realidad en el sistema de administración de memoria interna de Python. También tienen optimizaciones especiales para bloques de memoria más pequeños, lo que acelera su asignación al evitar costosas llamadas al sistema operativo.

En consecuencia, puede escribir como se muestra a continuación, que parece manejar este caso de uso como se pretende:

 from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free cdef class Variable: cdef unsigned int Length cdef char * Array def __cinit__(self, var,size_t length): self.Length = length self.Array = PyMem_Malloc(length * sizeof(char)) #as in docs, a good practice if not self.Array: raise MemoryError() for i in range(self.Length): self.Array[i] = (var >> (8 * i)) def __dealloc__(self): PyMem_Free(self.Array) 

La respuesta de @rll hace un buen trabajo de limpiar el código y “hacer todo correctamente” (lo más importante es la desasignación de memoria que faltaba en __dealloc__ en la pregunta).

El problema real que causa el error es que no has cimport ed malloc . Debido a esto, Cython asume que malloc es una función de Python, que devuelve un objeto de Python que desea convertir a un char* . En la parte superior de su archivo, agregue

 from libc.stdlib cimport malloc, free 

y funcionará. O, alternativamente, use PyMem_Malloc (cimportándolo) como lo hace @rll y eso también funcionará bien.