python - ¿Por qué debería ser necesario Py_INCREF(Py_None) antes de devolver Py_None en C?
reference-counting (2)
¿Por qué debería ser necesario Py_INCREF (Py_None) antes de devolver Py_None en C de la siguiente manera?
Py_INCREF(Py_None);
return Py_None;
Si se omite Py_INCREF (Py_None), ¿qué sucederá?
La falta de un Py_INCREF
resultará en un conteo incorrecto de referencias para Py_None
, lo que puede llevar al intérprete a desasignar Py_None
. Dado que Py_None
se asigna estáticamente en el archivo Objects/object.c
:
PyObject _Py_NoneStruct = {
_PyObject_EXTRA_INIT
1, &PyNone_Type
};
Y en Include/object.h
existe el define:
#define Py_None (&_Py_NoneStruct)
Entonces, lo que sucederá es que el intérprete se bloqueará con un error fatal:
Fatal Python error: deallocating None
La cual es generada por la función none_dealloc
en Objects/object.c
:
/* ARGUSED */
static void
none_dealloc(PyObject* ignore)
{
/* This should never get called, but we also don''t want to SEGV if
* we accidentally decref None out of existence.
*/
Py_FatalError("deallocating None");
}
Como se indica en ese comentario, si NoneType
no tuviera su propia función de desasignación, obtendría un Fallo de segmentación, ya que se realizaría una llamada free
en la pila.
Puede probar esto copiando el ejemplo en el tutorial , agregando una llamada a Py_DECREF(Py_None)
en la función Noddy_name
, Noddy_name
la extensión y haga un bucle llamando a ese método.
En el caso general, una cuenta de referencia de 0
puede hacer que el programa falle de muchas maneras diferentes.
En particular, Python es libre de reutilizar la memoria utilizada por los objetos que se desasignaron, lo que significa que, de repente, todas las referencias a un objeto pueden convertirse en referencias a un objeto aleatorio (oa una ubicación de memoria vacía), y podría ver cosas como :
>>> None #or whatever object that was deallocated
<ARandomObjectYouNeverSawBefore object at ...>
(Esto realmente me happened a veces, al escribir extensiones de C. Algunos objetos se convirtieron en buffers de solo lectura en momentos aleatorios debido a llamadas perdidas a Py_INCREF
).
En otras situaciones, se podrían generar diferentes tipos de errores, o el intérprete podría fallar o segfault.
Py_None
es realmente solo otro objeto de Python, excepto sin métodos.
Python contará las referencias a cualquier PyObject*
. No importa si es una cadena, un entero o ninguno.
Si no incrementa el recuento de referencias, el intérprete de Python finalmente descartará el objeto después de que su recuento de referencias llegue a 0
, pensando que no hay ningún puntero al objeto. Esto significa que la próxima vez que intente hacer algo con el valor de retorno, estará siguiendo un puntero a una ubicación en la memoria que no se garantiza que contenga Py_None
(error, valor extraño, falla de segmentación, etc.).
Hay alternativas a tener que recordar usar Py_INCREF(Py_None)
:
return Py_BuildValue("");
o
Py_RETURN_NONE;