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

Поддержка 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, если объект неизменяемый.

int (*inquiry)(PyObject *self)

Отбросить ссылки, которые могли создать ссылочные циклы. Неизменяемые объекты не должны определять данный метод, поскольку они никогда не могут напрямую создавать ссылочные циклы. Обратите внимание, что объект должен оставаться действительным после вызова этого метода (не вызывайте просто Py_DECREF() по ссылке). Сборщик вызовет данный метод, если обнаружит, что данный объект участвует в цикле обращения.