Поддержка циклической сборки мусора

Python поддерживает обнаружения и сборку мусора, которая включает в себя циклические ссылки, требует поддержки от типов объектов, которые являются «контейнерами» для других объектов, которые также могут быть контейнерами. Типы, которые не хранят ссылки на другие объекты или которые хранят ссылки только на атомарные типы (такие как числа или строки), не должны обеспечивать явную поддержку сбори мусора.

Для создания контейнерного типа поле tp_flags объекта типа должно включать Py_TPFLAGS_HAVE_GC и обеспечивать реализацию tp_traverse обработчик. Если сущности типа являются изменяемыми, также должна быть предусмотрена tp_clear реализация.

Py_TPFLAGS_HAVE_GC

Объекты с типом с таким набором флагов должны соответствовать правилам, описанным здесь. Для удобства эти объекты будут именоваться контейнерными объектами.

Конструкторы для типов контейнеров должны соответствовать двум правилам:

  1. Память объекта должна быть аллоцирована с помощью PyObject_GC_New() или PyObject_GC_NewVar().
  2. После инициализации всех полей, которые могут содержать ссылки на другие контейнеры, необходимо вызвать PyObject_GC_Track().
TYPE* PyObject_GC_New(TYPE, PyTypeObject *type)

Аналогично PyObject_New(), но для контейнерных объектов с установленным флагом Py_TPFLAGS_HAVE_GC.

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

Аналогично PyObject_NewVar(), но для контейнерных объектов с установленным флагом Py_TPFLAGS_HAVE_GC.

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

Изменение размера объекта, аллоцированный PyObject_NewVar(). Возвращает измененный размер объекта или NULL при сбое. op не должны отслеживаться коллектором.

void PyObject_GC_Track(PyObject *op)

Добавляет op объекта в набор объектов-контейнеров, отслеживаемых коллектором. Коллектор может выполняться в неожиданное время, поэтому объекты должны быть действительными при отслеживании. Этот вызов следует выполнить после того, как все поля, за которыми следует tp_traverse обработчик, станут действительными, как правило, ближе к концу конструктора.

Аналогично, деаллокатор для объекта должен соответствовать аналогичной паре правил:

  1. Перед признанием недействительными полей, ссылающихся на другие контейнеры, необходимо вызвать PyObject_GC_UnTrack().
  2. Память объекта должна быть освобождена с помощью PyObject_GC_Del().
void PyObject_GC_Del(void *op)

Высвобождение аллоцированной памяти для объекта с помощью PyObject_GC_New() или PyObject_GC_NewVar().

void PyObject_GC_UnTrack(void *op)

Удалить op объекта из набора объектов-контейнеров, отслеживаемых коллектором. Обратите внимание, что PyObject_GC_Track() можно снова вызвать для этого объекта, чтобы добавить его обратно в набор отслеживаемых объектов. Деаллокатор (tp_dealloc обработчик) должен назвать это для объекта перед любым из полей, используемый tp_traverse обработчик становятся недействительными.

Изменено в версии 3.8: Макросы _PyObject_GC_TRACK() и _PyObject_GC_UNTRACK() удалены из публичного C API.

tp_traverse обработчик принимает параметр функции следующего типа:

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

Тип функции посетителя, переданной tp_traverse обработчику. Функция должна быть вызвана с просматриваемым объектом как object и третий параметр к tp_traverse обработчик как arg. Ядро Python использует несколько функций посетителей для реализации циклического обнаружения мусора; не ожидается, что пользователям потребуется писать собственные функции посетителей.

Тип tp_traverse обработчик должен быть следующим:

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

Функция обхода для объекта-контейнера. Реализации должны вызывать функцию visit для каждого объекта, непосредственно содержащегося в self, причем параметры visit являются содержащимся объектом, а arg значение передаются в обработчик. Функцию visit нельзя вызывать с аргументом объекта NULL. Если visit возвращает ненулевой значение этот значение должен быть возвращенный немедленно.

Для упрощения записи tp_traverse обработчики предусмотрен макрос Py_VISIT(). Чтобы использовать этот макрос, реализация tp_traverse должна назвать его аргументы точно visit и arg:

void Py_VISIT(PyObject *o)

Если o не NULL, вызвать visit колбэк, с аргументами o и arg. Если visit возвращает ненулевой значение, то возвращает его. Используя этот макрос, tp_traverse обработчики выглядеть как:

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

tp_clear обработчик должен иметь тип inquiry или NULL, если объект является неизменяемым.