Apoyo a la recolección de basura cíclica

El soporte de Python para detectar y recolectar basura que involucra referencias circulares requiere el soporte de tipos de objetos que son «contenedores» para otros objetos que también pueden ser contenedores. Los tipos que no almacenan referencias a otros objetos, o que solo almacenan referencias a tipos atómicos (como números o cadenas), no necesitan proporcionar ningún soporte explícito para la recolección de basura.

Para crear un tipo de contenedor, el campo tp_flags del objeto tipo debe incluir Py_TPFLAGS_HAVE_GC y proporcionar una implementación del manejador tp_traverse . Si las instancias del tipo son mutables, también se debe proporcionar una implementación a tp_clear.

Py_TPFLAGS_HAVE_GC

Los objetos con un tipo con este indicador establecido deben cumplir con las reglas documentadas aquí. Por conveniencia, estos objetos se denominarán objetos contenedor.

Los constructores para tipos de contenedores deben cumplir con dos reglas:

  1. La memoria para el objeto debe asignarse usando PyObject_GC_New() o PyObject_GC_NewVar().

  2. Una vez que se inicializan todos los campos que pueden contener referencias a otros contenedores, debe llamar a PyObject_GC_Track().

    Advertencia

    If a type adds the Py_TPFLAGS_HAVE_GC, then it must implement at least a tp_traverse handler or explicitly use one from its subclass or subclasses.

    When calling PyType_Ready() or some of the APIs that indirectly call it like PyType_FromSpecWithBases() or PyType_FromSpec() the interpreter will automatically populate the tp_flags, tp_traverse and tp_clear fields if the type inherits from a class that implements the garbage collector protocol and the child class does not include the Py_TPFLAGS_HAVE_GC flag.

TYPE* PyObject_GC_New(TYPE, PyTypeObject *type)

Análogo a PyObject_New() pero para objetos de contenedor con el flag Py_TPFLAGS_HAVE_GC establecido.

TYPE* PyObject_GC_NewVar(TYPE, PyTypeObject *type, Py_ssize_t size)

Análogo a PyObject_NewVar() pero para objetos de contenedor con el flag Py_TPFLAGS_HAVE_GC establecido.

TYPE* PyObject_GC_Resize(TYPE, PyVarObject *op, Py_ssize_t newsize)

Cambia el tamaño de un objeto asignado por PyObject_NewVar(). Retorna el objeto redimensionado o NULL en caso de falla. op aún no debe ser rastreado por el recolector de basura.

void PyObject_GC_Track(PyObject *op)

Agrega el objeto op al conjunto de objetos contenedor seguidos por el recolector de basura. El recolector puede ejecutarse en momentos inesperados, por lo que los objetos deben ser válidos durante el seguimiento. Esto debería llamarse una vez que todos los campos seguidos por tp_traverse se vuelven válidos, generalmente cerca del final del constructor.

int PyObject_IS_GC(PyObject *obj)

Returns non-zero if the object implements the garbage collector protocol, otherwise returns 0.

The object cannot be tracked by the garbage collector if this function returns 0.

int PyObject_GC_IsTracked(PyObject *op)

Returns 1 if the object type of op implements the GC protocol and op is being currently tracked by the garbage collector and 0 otherwise.

This is analogous to the Python function gc.is_tracked().

Nuevo en la versión 3.9.

int PyObject_GC_IsFinalized(PyObject *op)

Returns 1 if the object type of op implements the GC protocol and op has been already finalized by the garbage collector and 0 otherwise.

This is analogous to the Python function gc.is_finalized().

Nuevo en la versión 3.9.

Del mismo modo, el desasignador (deallocator) para el objeto debe cumplir con un par similar de reglas:

  1. Antes de invalidar los campos que se refieren a otros contenedores, debe llamarse PyObject_GC_UnTrack().

  2. La memoria del objeto debe ser desasignada (deallocated) usando PyObject_GC_Del().

void PyObject_GC_Del(void *op)

Libera memoria asignada a un objeto usando PyObject_GC_New() o PyObject_GC_NewVar().

void PyObject_GC_UnTrack(void *op)

Elimina el objeto op del conjunto de objetos contenedor rastreados por el recolector de basura. Tenga en cuenta que PyObject_GC_Track() puede ser llamado nuevamente en este objeto para agregarlo nuevamente al conjunto de objetos rastreados. El desasignador (el manejador tp_dealloc) debería llamarlo para el objeto antes de que cualquiera de los campos utilizados por el manejador tp_traverse no sea válido.

Distinto en la versión 3.8: Los macros _PyObject_GC_TRACK() y _PyObject_GC_UNTRACK() se han eliminado de la API pública de C.

El manejador tp_traverse acepta un parámetro de función de este tipo:

int (*visitproc)(PyObject *object, void *arg)

Tipo de la función visitante que se pasa al manejador tp_traverse. La función debe llamarse con un objeto para atravesar como object y el tercer parámetro para el manejador tp_traverse como arg. El núcleo de Python utiliza varias funciones visitantes para implementar la detección de basura cíclica; No se espera que los usuarios necesiten escribir sus propias funciones visitante.

El manejador tp_traverse debe tener el siguiente tipo:

int (*traverseproc)(PyObject *self, visitproc visit, void *arg)

Función transversal para un objeto contenedor. Las implementaciones deben llamar a la función visit para cada objeto directamente contenido por self, siendo los parámetros a visit el objeto contenido y el valor arg pasado al controlador. La función visit no debe llamarse con un argumento de objeto NULL. Si visit retorna un valor distinto de cero, ese valor debe retornarse inmediatamente.

Para simplificar la escritura de los manejadores tp_traverse, se proporciona un macro a Py_VISIT(). Para usar este macro, la implementación tp_traverse debe nombrar sus argumentos exactamente visit y arg:

void Py_VISIT(PyObject *o)

Si o no es NULL, llama a la devolución de llamada (callback) visit, con argumentos o y arg. Si visit retorna un valor distinto de cero, lo retorna. Usando este macro, los manejadores tp_traverse tienen el siguiente aspecto:

static int
my_traverse(Noddy *self, visitproc visit, void *arg)
{
    Py_VISIT(self->foo);
    Py_VISIT(self->bar);
    return 0;
}

El manejador tp_clear debe ser del tipo query, o NULL si el objeto es inmutable.

int (*inquiry)(PyObject *self)

Descarta referencias que pueden haber creado ciclos de referencia. Los objetos inmutables no tienen que definir este método ya que nunca pueden crear directamente ciclos de referencia. Tenga en cuenta que el objeto aún debe ser válido después de llamar a este método (no solo llame a Py_DECREF() en una referencia). El recolector de basura llamará a este método si detecta que este objeto está involucrado en un ciclo de referencia.