Подтвердить что ты не робот

Зачем нужно Py_INCREF (Py_None) перед возвратом Py_None в C?

Зачем нужно Py_INCREF (Py_None) перед возвратом Py_None в C следующим образом?

Py_INCREF(Py_None);
return Py_None;

Если Py_INCREF (Py_None) опущен, что произойдет?

4b9b3361

Ответ 1

Отсутствие Py_INCREF приведет к некорректному подсчету ссылок для Py_None, что может заставить интерпретатор освободить Py_None. Поскольку Py_None выделяется статически в файле Objects/object.c:

PyObject _Py_NoneStruct = {
  _PyObject_EXTRA_INIT
  1, &PyNone_Type
};

И в Include/object.h есть определение:

#define Py_None (&_Py_NoneStruct)

Итак, что произойдет, заключается в том, что интерпретатор выйдет из строя с фатальной ошибкой:

Fatal Python error: deallocating None

Что генерируется функцией none_dealloc в 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");
}

Как указано в этом комментарии, если NoneType не имеет собственной функции дезактивации, вы получите ошибку сегментации, так как вызов free будет выполнен в стеке.

Вы можете протестировать это копирование примера в учебник, добавив вызов Py_DECREF(Py_None) в функцию Noddy_name, создайте расширение и сделать цикл, вызывающий этот метод.


В общем случае счетчик ссылок 0 может привести к сбою программы разными способами.

В частности, python может повторно использовать память, используемую объектами, которые были освобождены, что означает, что внезапно каждая ссылка на объект может стать ссылкой на случайный объект (или на пустую ячейку памяти), и вы могли бы см. такие вещи, как:

>>> None   #or whatever object that was deallocated
<ARandomObjectYouNeverSawBefore object at ...>

(Это фактически происходило со мной иногда при написании расширений C. Некоторые объекты, которые в случайном порядке переходили в буферы только для чтения из-за отсутствия вызовов Py_INCREF).

В других ситуациях могут возникать различные ошибки, или интерпретатор может сбой или segfault.

Ответ 2

Py_None - это действительно еще один объект Python, за исключением без методов.

Python будет считать ссылки на любой PyObject*. Не имеет значения, является ли это строкой, целым числом или None.

Если вы не увеличиваете счетчик ссылок, интерпретатор Python в конечном итоге отбросит объект после того, как его счетчик ссылок достигнет 0, считая, что нет никаких указателей на объект. Это означает, что в следующий раз, когда вы попытаетесь что-то сделать с возвращаемым значением, вы будете следовать указателю на место в памяти, в котором не гарантируется сохранение Py_None (ошибка, странное значение, ошибка сегментации и т.д.).

Есть альтернативы необходимости запоминать использование Py_INCREF(Py_None):

return Py_BuildValue("");

или

Py_RETURN_NONE;